On 15.09.2013 15:02, and...@tao11.riddles.org.uk wrote:
The following bug has been logged on the website:
Bug reference: 8453
Logged by: Andrew Gierth
Email address: and...@tao11.riddles.org.uk
PostgreSQL version: 9.3.0
Operating system: any
Description:
The first snprintf in writeTimeLineHistoryFile in receivelog.c accesses
uninitialized data in the "path" variable, thus creating the .tmp file in a
random place (usually the current dir, leading to unexpected EXDEV errors on
the rename).
Ouch, that was a silly bug!
Also, receivexlog is ignoring .partial and .history files when determining
which timeline to start streaming from, which means that if there are two
timeline changes that are not separated by a WAL segment switch, it will
fail to operate due to attempting to start from a too-old timeline (for
which xlogs are not available on the server).
There's nothing we can do with .history files here. The point is to find
out how far we have already received WAL, and the presence of a .history
file doesn't tell you anything about that.
There is a comment about .partial files though:
/*
* Check if the filename looks like an xlog file, or a .partial
file.
* Xlog files are always 24 characters, and .partial files are
32
* characters.
*/
if (strlen(dirent->d_name) != 24 ||
strspn(dirent->d_name, "0123456789ABCDEF") != 24)
continue;
The comment says that .partial files are taken into account, but the
code doesn't match the comment.
Attached is a patch to fix both of these issues. I'm too tired right now
to thoroughly test it and commit, so I'll get back to this tomorrow.
Meanwhile, please take a look and let me know if you can see something
wrong.
- Heikki
diff --git a/src/bin/pg_basebackup/pg_receivexlog.c b/src/bin/pg_basebackup/pg_receivexlog.c
index 787a395..ca89438 100644
--- a/src/bin/pg_basebackup/pg_receivexlog.c
+++ b/src/bin/pg_basebackup/pg_receivexlog.c
@@ -121,6 +121,7 @@ FindStreamingStart(uint32 *tli)
struct dirent *dirent;
XLogSegNo high_segno = 0;
uint32 high_tli = 0;
+ bool high_ispartial = false;
dir = opendir(basedir);
if (dir == NULL)
@@ -132,20 +133,33 @@ FindStreamingStart(uint32 *tli)
while ((dirent = readdir(dir)) != NULL)
{
- char fullpath[MAXPGPATH];
- struct stat statbuf;
uint32 tli;
unsigned int log,
seg;
XLogSegNo segno;
+ bool ispartial;
/*
* Check if the filename looks like an xlog file, or a .partial file.
* Xlog files are always 24 characters, and .partial files are 32
* characters.
*/
- if (strlen(dirent->d_name) != 24 ||
- strspn(dirent->d_name, "0123456789ABCDEF") != 24)
+ if (strlen(dirent->d_name) == 24)
+ {
+ if (strspn(dirent->d_name, "0123456789ABCDEF") != 24)
+ continue;
+ ispartial = false;
+ }
+ else if (strlen(dirent->d_name) == 32)
+ {
+ if (strspn(dirent->d_name, "0123456789ABCDEF") != 24)
+ continue;
+ if (strcmp(&dirent->d_name[24], ".partial") != 0)
+ continue;
+
+ ispartial = true;
+ }
+ else
continue;
/*
@@ -160,31 +174,40 @@ FindStreamingStart(uint32 *tli)
}
segno = ((uint64) log) << 32 | seg;
- /* Check if this is a completed segment or not */
- snprintf(fullpath, sizeof(fullpath), "%s/%s", basedir, dirent->d_name);
- if (stat(fullpath, &statbuf) != 0)
+ /*
+ * Check that the segment has the right size, if it's supposed to be
+ * completed.
+ */
+ if (!ispartial)
{
- fprintf(stderr, _("%s: could not stat file \"%s\": %s\n"),
- progname, fullpath, strerror(errno));
- disconnect_and_exit(1);
- }
+ struct stat statbuf;
+ char fullpath[MAXPGPATH];
- if (statbuf.st_size == XLOG_SEG_SIZE)
- {
- /* Completed segment */
- if (segno > high_segno || (segno == high_segno && tli > high_tli))
+ snprintf(fullpath, sizeof(fullpath), "%s/%s", basedir, dirent->d_name);
+ if (stat(fullpath, &statbuf) != 0)
+ {
+ fprintf(stderr, _("%s: could not stat file \"%s\": %s\n"),
+ progname, fullpath, strerror(errno));
+ disconnect_and_exit(1);
+ }
+
+ if (statbuf.st_size != XLOG_SEG_SIZE)
{
- high_segno = segno;
- high_tli = tli;
+ fprintf(stderr,
+ _("%s: segment file \"%s\" has incorrect size %d, skipping\n"),
+ progname, dirent->d_name, (int) statbuf.st_size);
continue;
}
}
- else
+
+ /* Looks like a valid segment. Remember that we saw it */
+ if ((segno > high_segno) ||
+ (segno == high_segno && tli > high_tli) ||
+ (segno == high_segno && tli == high_tli && high_ispartial && !ispartial))
{
- fprintf(stderr,
- _("%s: segment file \"%s\" has incorrect size %d, skipping\n"),
- progname, dirent->d_name, (int) statbuf.st_size);
- continue;
+ high_segno = segno;
+ high_tli = tli;
+ high_ispartial = ispartial;
}
}
@@ -195,10 +218,12 @@ FindStreamingStart(uint32 *tli)
XLogRecPtr high_ptr;
/*
- * Move the starting pointer to the start of the next segment, since
- * the highest one we've seen was completed.
+ * Move the starting pointer to the start of the next segment, if
+ * the highest one we saw was completed. Otherwise start streaming
+ * from the beginning of the .partial segment.
*/
- high_segno++;
+ if (!high_ispartial)
+ high_segno++;
XLogSegNoOffsetToRecPtr(high_segno, 0, high_ptr);
diff --git a/src/bin/pg_basebackup/receivelog.c b/src/bin/pg_basebackup/receivelog.c
index d56a4d7..bee663a 100644
--- a/src/bin/pg_basebackup/receivelog.c
+++ b/src/bin/pg_basebackup/receivelog.c
@@ -166,8 +166,7 @@ close_walfile(char *basedir, char *partial_suffix)
walfile = -1;
/*
- * Rename the .partial file only if we've completed writing the whole
- * segment or segment_complete is true.
+ * If we finished writing a .partial file, rename it into place.
*/
if (currpos == XLOG_SEG_SIZE && partial_suffix)
{
@@ -294,6 +293,8 @@ writeTimeLineHistoryFile(char *basedir, TimeLineID tli, char *filename, char *co
char histfname[MAXFNAMELEN];
int fd;
+ snprintf(path, sizeof(path), "%s/%s", basedir, histfname);
+
/*
* Check that the server's idea of how timeline history files should be
* named matches ours.
@@ -356,8 +357,6 @@ writeTimeLineHistoryFile(char *basedir, TimeLineID tli, char *filename, char *co
/*
* Now move the completed history file into place with its final name.
*/
-
- snprintf(path, sizeof(path), "%s/%s", basedir, histfname);
if (rename(tmppath, path) < 0)
{
fprintf(stderr, _("%s: could not rename file \"%s\" to \"%s\": %s\n"),
--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs