mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Use pg_parse_lsn() for server-supplied LSNs
Commit d6bf0ab170 introduced pg_parse_lsn() to validate LSNs given on
the command line of pg_waldump, pg_recvlogical, and pg_receivewal.
The remaining sscanf("%X/%08X") call sites under src/bin parse LSNs
that arrive in server responses, timeline history files, and
backup_label files. sscanf() accepts several forms that pg_lsn input
rejects and can silently continue with a different location than the
input text: a first component wider than eight hex digits wraps
around, a wider second component is truncated, and leading
whitespace, signs, "0x" prefixes, and trailing characters are
consumed or ignored.
Convert those call sites as well. The two call sites that read a
location out of a longer line isolate it by temporarily terminating
the string at the next whitespace character, so that they can use
pg_parse_lsn() like the others. Each tool keeps its existing error
message.
Malformed metadata now fails with each tool's existing error instead
of silently proceeding with a different location. Two error paths
shift: pg_rewind's history-file parser now rejects trailing
characters attached to a switchpoint, which used to be ignored, and a
malformed backup_label location now fails pg_combinebackup's "could
not parse" check rather than its "improper terminator" check.
Author: Zexin Li <lizi.openmind@gmail.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Discussion: https://postgr.es/m/CAAP6ZkS_3OH3yhhAGK6vu+2V1C2Hv4K6SpRuZL415R-gxjdTSg@mail.gmail.com
This commit is contained in:
@@ -32,6 +32,7 @@
|
|||||||
#include "common/file_perm.h"
|
#include "common/file_perm.h"
|
||||||
#include "common/file_utils.h"
|
#include "common/file_utils.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#include "common/pg_parse_lsn.h"
|
||||||
#include "fe_utils/option_utils.h"
|
#include "fe_utils/option_utils.h"
|
||||||
#include "fe_utils/recovery_gen.h"
|
#include "fe_utils/recovery_gen.h"
|
||||||
#include "getopt_long.h"
|
#include "getopt_long.h"
|
||||||
@@ -482,17 +483,14 @@ reached_end_position(XLogRecPtr segendpos, uint32 timeline,
|
|||||||
{
|
{
|
||||||
ssize_t nread;
|
ssize_t nread;
|
||||||
char xlogend[64] = {0};
|
char xlogend[64] = {0};
|
||||||
uint32 hi,
|
|
||||||
lo;
|
|
||||||
|
|
||||||
nread = read(bgpipe[0], xlogend, sizeof(xlogend) - 1);
|
nread = read(bgpipe[0], xlogend, sizeof(xlogend) - 1);
|
||||||
if (nread < 0)
|
if (nread < 0)
|
||||||
pg_fatal("could not read from ready pipe: %m");
|
pg_fatal("could not read from ready pipe: %m");
|
||||||
|
|
||||||
if (sscanf(xlogend, "%X/%08X", &hi, &lo) != 2)
|
if (!pg_parse_lsn(xlogend, &xlogendptr))
|
||||||
pg_fatal("could not parse write-ahead log location \"%s\"",
|
pg_fatal("could not parse write-ahead log location \"%s\"",
|
||||||
xlogend);
|
xlogend);
|
||||||
xlogendptr = ((uint64) hi) << 32 | lo;
|
|
||||||
has_xlogendptr = 1;
|
has_xlogendptr = 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -620,8 +618,6 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier,
|
|||||||
int wal_compress_level)
|
int wal_compress_level)
|
||||||
{
|
{
|
||||||
logstreamer_param *param;
|
logstreamer_param *param;
|
||||||
uint32 hi,
|
|
||||||
lo;
|
|
||||||
char statusdir[MAXPGPATH];
|
char statusdir[MAXPGPATH];
|
||||||
|
|
||||||
param = pg_malloc0_object(logstreamer_param);
|
param = pg_malloc0_object(logstreamer_param);
|
||||||
@@ -631,10 +627,9 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier,
|
|||||||
param->wal_compress_level = wal_compress_level;
|
param->wal_compress_level = wal_compress_level;
|
||||||
|
|
||||||
/* Convert the starting position */
|
/* Convert the starting position */
|
||||||
if (sscanf(startpos, "%X/%08X", &hi, &lo) != 2)
|
if (!pg_parse_lsn(startpos, ¶m->startptr))
|
||||||
pg_fatal("could not parse write-ahead log location \"%s\"",
|
pg_fatal("could not parse write-ahead log location \"%s\"",
|
||||||
startpos);
|
startpos);
|
||||||
param->startptr = ((uint64) hi) << 32 | lo;
|
|
||||||
/* Round off to even segment position */
|
/* Round off to even segment position */
|
||||||
param->startptr -= XLogSegmentOffset(param->startptr, WalSegSz);
|
param->startptr -= XLogSegmentOffset(param->startptr, WalSegSz);
|
||||||
|
|
||||||
@@ -2216,8 +2211,6 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
|
|||||||
* casting to a different size on WIN64.
|
* casting to a different size on WIN64.
|
||||||
*/
|
*/
|
||||||
intptr_t bgchild_handle = bgchild;
|
intptr_t bgchild_handle = bgchild;
|
||||||
uint32 hi,
|
|
||||||
lo;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
@@ -2243,10 +2236,9 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
|
|||||||
* value directly in the variable, and then set the flag that says
|
* value directly in the variable, and then set the flag that says
|
||||||
* it's there.
|
* it's there.
|
||||||
*/
|
*/
|
||||||
if (sscanf(xlogend, "%X/%08X", &hi, &lo) != 2)
|
if (!pg_parse_lsn(xlogend, &xlogendptr))
|
||||||
pg_fatal("could not parse write-ahead log location \"%s\"",
|
pg_fatal("could not parse write-ahead log location \"%s\"",
|
||||||
xlogend);
|
xlogend);
|
||||||
xlogendptr = ((uint64) hi) << 32 | lo;
|
|
||||||
InterlockedIncrement(&has_xlogendptr);
|
InterlockedIncrement(&has_xlogendptr);
|
||||||
|
|
||||||
/* First wait for the thread to exit */
|
/* First wait for the thread to exit */
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "access/xlog_internal.h"
|
#include "access/xlog_internal.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#include "common/pg_parse_lsn.h"
|
||||||
#include "libpq-fe.h"
|
#include "libpq-fe.h"
|
||||||
#include "libpq/protocol.h"
|
#include "libpq/protocol.h"
|
||||||
#include "receivelog.h"
|
#include "receivelog.h"
|
||||||
@@ -704,9 +705,6 @@ error:
|
|||||||
static bool
|
static bool
|
||||||
ReadEndOfStreamingResult(PGresult *res, XLogRecPtr *startpos, uint32 *timeline)
|
ReadEndOfStreamingResult(PGresult *res, XLogRecPtr *startpos, uint32 *timeline)
|
||||||
{
|
{
|
||||||
uint32 startpos_xlogid,
|
|
||||||
startpos_xrecoff;
|
|
||||||
|
|
||||||
/*----------
|
/*----------
|
||||||
* The result set consists of one row and two columns, e.g:
|
* The result set consists of one row and two columns, e.g:
|
||||||
*
|
*
|
||||||
@@ -727,14 +725,12 @@ ReadEndOfStreamingResult(PGresult *res, XLogRecPtr *startpos, uint32 *timeline)
|
|||||||
}
|
}
|
||||||
|
|
||||||
*timeline = atoi(PQgetvalue(res, 0, 0));
|
*timeline = atoi(PQgetvalue(res, 0, 0));
|
||||||
if (sscanf(PQgetvalue(res, 0, 1), "%X/%08X", &startpos_xlogid,
|
if (!pg_parse_lsn(PQgetvalue(res, 0, 1), startpos))
|
||||||
&startpos_xrecoff) != 2)
|
|
||||||
{
|
{
|
||||||
pg_log_error("could not parse next timeline's starting point \"%s\"",
|
pg_log_error("could not parse next timeline's starting point \"%s\"",
|
||||||
PQgetvalue(res, 0, 1));
|
PQgetvalue(res, 0, 1));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*startpos = ((uint64) startpos_xlogid << 32) | startpos_xrecoff;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "common/connect.h"
|
#include "common/connect.h"
|
||||||
#include "common/file_perm.h"
|
#include "common/file_perm.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#include "common/pg_parse_lsn.h"
|
||||||
#include "common/string.h"
|
#include "common/string.h"
|
||||||
#include "datatype/timestamp.h"
|
#include "datatype/timestamp.h"
|
||||||
#include "port/pg_bswap.h"
|
#include "port/pg_bswap.h"
|
||||||
@@ -410,8 +411,6 @@ RunIdentifySystem(PGconn *conn, char **sysid, TimeLineID *starttli,
|
|||||||
XLogRecPtr *startpos, char **db_name)
|
XLogRecPtr *startpos, char **db_name)
|
||||||
{
|
{
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
uint32 hi,
|
|
||||||
lo;
|
|
||||||
|
|
||||||
/* Check connection existence */
|
/* Check connection existence */
|
||||||
Assert(conn != NULL);
|
Assert(conn != NULL);
|
||||||
@@ -445,7 +444,7 @@ RunIdentifySystem(PGconn *conn, char **sysid, TimeLineID *starttli,
|
|||||||
/* Get LSN start position if necessary */
|
/* Get LSN start position if necessary */
|
||||||
if (startpos != NULL)
|
if (startpos != NULL)
|
||||||
{
|
{
|
||||||
if (sscanf(PQgetvalue(res, 0, 2), "%X/%08X", &hi, &lo) != 2)
|
if (!pg_parse_lsn(PQgetvalue(res, 0, 2), startpos))
|
||||||
{
|
{
|
||||||
pg_log_error("could not parse write-ahead log location \"%s\"",
|
pg_log_error("could not parse write-ahead log location \"%s\"",
|
||||||
PQgetvalue(res, 0, 2));
|
PQgetvalue(res, 0, 2));
|
||||||
@@ -453,7 +452,6 @@ RunIdentifySystem(PGconn *conn, char **sysid, TimeLineID *starttli,
|
|||||||
PQclear(res);
|
PQclear(res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*startpos = ((uint64) hi) << 32 | lo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get database name, only available in 9.4 and newer versions */
|
/* Get database name, only available in 9.4 and newer versions */
|
||||||
@@ -549,17 +547,13 @@ GetSlotInformation(PGconn *conn, const char *slot_name,
|
|||||||
/* restart LSN */
|
/* restart LSN */
|
||||||
if (!PQgetisnull(res, 0, 1))
|
if (!PQgetisnull(res, 0, 1))
|
||||||
{
|
{
|
||||||
uint32 hi,
|
if (!pg_parse_lsn(PQgetvalue(res, 0, 1), &lsn_loc))
|
||||||
lo;
|
|
||||||
|
|
||||||
if (sscanf(PQgetvalue(res, 0, 1), "%X/%08X", &hi, &lo) != 2)
|
|
||||||
{
|
{
|
||||||
pg_log_error("could not parse restart_lsn \"%s\" for replication slot \"%s\"",
|
pg_log_error("could not parse restart_lsn \"%s\" for replication slot \"%s\"",
|
||||||
PQgetvalue(res, 0, 1), slot_name);
|
PQgetvalue(res, 0, 1), slot_name);
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
lsn_loc = ((uint64) hi) << 32 | lo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* current TLI */
|
/* current TLI */
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "backup_label.h"
|
#include "backup_label.h"
|
||||||
#include "common/file_perm.h"
|
#include "common/file_perm.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#include "common/pg_parse_lsn.h"
|
||||||
#include "write_manifest.h"
|
#include "write_manifest.h"
|
||||||
|
|
||||||
static int get_eol_offset(StringInfo buf);
|
static int get_eol_offset(StringInfo buf);
|
||||||
@@ -234,28 +235,29 @@ line_starts_with(char *s, char *e, char *match, char **sout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse an LSN starting at s and not stopping at or before e. The return value
|
* Parse an LSN token starting at s and ending before the next whitespace
|
||||||
* is true on success and otherwise false. On success, stores the result into
|
* character or e. The return value is true on success and otherwise false.
|
||||||
* *lsn and sets *c to the first character that is not part of the LSN.
|
* On success, stores the result into *lsn and sets *c to the first character
|
||||||
|
* after the token.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
parse_lsn(char *s, char *e, XLogRecPtr *lsn, char **c)
|
parse_lsn(char *s, char *e, XLogRecPtr *lsn, char **c)
|
||||||
{
|
{
|
||||||
char save = *e;
|
char save = *e;
|
||||||
int nchars;
|
char *token_end;
|
||||||
|
char save_token_end;
|
||||||
bool success;
|
bool success;
|
||||||
unsigned hi;
|
|
||||||
unsigned lo;
|
|
||||||
|
|
||||||
*e = '\0';
|
*e = '\0';
|
||||||
success = (sscanf(s, "%X/%08X%n", &hi, &lo, &nchars) == 2);
|
token_end = s + strcspn(s, " \t\n\r\f\v");
|
||||||
|
save_token_end = *token_end;
|
||||||
|
*token_end = '\0';
|
||||||
|
success = pg_parse_lsn(s, lsn);
|
||||||
|
*token_end = save_token_end;
|
||||||
*e = save;
|
*e = save;
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
*c = token_end;
|
||||||
*lsn = ((XLogRecPtr) hi) << 32 | (XLogRecPtr) lo;
|
|
||||||
*c = s + nchars;
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "catalog/pg_type_d.h"
|
#include "catalog/pg_type_d.h"
|
||||||
#include "common/connect.h"
|
#include "common/connect.h"
|
||||||
|
#include "common/pg_parse_lsn.h"
|
||||||
#include "file_ops.h"
|
#include "file_ops.h"
|
||||||
#include "filemap.h"
|
#include "filemap.h"
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
@@ -209,17 +210,13 @@ libpq_get_current_wal_insert_lsn(rewind_source *source)
|
|||||||
{
|
{
|
||||||
PGconn *conn = ((libpq_source *) source)->conn;
|
PGconn *conn = ((libpq_source *) source)->conn;
|
||||||
XLogRecPtr result;
|
XLogRecPtr result;
|
||||||
uint32 hi;
|
|
||||||
uint32 lo;
|
|
||||||
char *val;
|
char *val;
|
||||||
|
|
||||||
val = run_simple_query(conn, "SELECT pg_current_wal_insert_lsn()");
|
val = run_simple_query(conn, "SELECT pg_current_wal_insert_lsn()");
|
||||||
|
|
||||||
if (sscanf(val, "%X/%08X", &hi, &lo) != 2)
|
if (!pg_parse_lsn(val, &result))
|
||||||
pg_fatal("unrecognized result \"%s\" for current WAL insert location", val);
|
pg_fatal("unrecognized result \"%s\" for current WAL insert location", val);
|
||||||
|
|
||||||
result = ((uint64) hi) << 32 | lo;
|
|
||||||
|
|
||||||
pg_free(val);
|
pg_free(val);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "postgres_fe.h"
|
#include "postgres_fe.h"
|
||||||
|
|
||||||
#include "access/timeline.h"
|
#include "access/timeline.h"
|
||||||
|
#include "common/pg_parse_lsn.h"
|
||||||
#include "pg_rewind.h"
|
#include "pg_rewind.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -44,10 +45,13 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
|
|||||||
while (!lastline)
|
while (!lastline)
|
||||||
{
|
{
|
||||||
char *ptr;
|
char *ptr;
|
||||||
|
char *token_end;
|
||||||
|
char save;
|
||||||
TimeLineID tli;
|
TimeLineID tli;
|
||||||
uint32 switchpoint_hi;
|
XLogRecPtr switchpoint;
|
||||||
uint32 switchpoint_lo;
|
bool valid_switchpoint = false;
|
||||||
int nfields;
|
int nchars;
|
||||||
|
size_t nspaces;
|
||||||
|
|
||||||
fline = bufptr;
|
fline = bufptr;
|
||||||
while (*bufptr && *bufptr != '\n')
|
while (*bufptr && *bufptr != '\n')
|
||||||
@@ -66,16 +70,33 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
|
|||||||
if (*ptr == '\0' || *ptr == '#')
|
if (*ptr == '\0' || *ptr == '#')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
nfields = sscanf(fline, "%u\t%X/%08X", &tli, &switchpoint_hi, &switchpoint_lo);
|
if (sscanf(fline, "%u%n", &tli, &nchars) != 1)
|
||||||
|
|
||||||
if (nfields < 1)
|
|
||||||
{
|
{
|
||||||
/* expect a numeric timeline ID as first field of line */
|
/* expect a numeric timeline ID as first field of line */
|
||||||
pg_log_error("syntax error in history file: %s", fline);
|
pg_log_error("syntax error in history file: %s", fline);
|
||||||
pg_log_error_detail("Expected a numeric timeline ID.");
|
pg_log_error_detail("Expected a numeric timeline ID.");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (nfields != 3)
|
|
||||||
|
/* the switchpoint location follows, separated by whitespace */
|
||||||
|
ptr = fline + nchars;
|
||||||
|
nspaces = strspn(ptr, " \t\n\r\f\v");
|
||||||
|
if (nspaces > 0)
|
||||||
|
{
|
||||||
|
ptr += nspaces;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* isolate the location from the rest of the line before parsing
|
||||||
|
* it
|
||||||
|
*/
|
||||||
|
token_end = ptr + strcspn(ptr, " \t\n\r\f\v");
|
||||||
|
save = *token_end;
|
||||||
|
*token_end = '\0';
|
||||||
|
valid_switchpoint = pg_parse_lsn(ptr, &switchpoint);
|
||||||
|
*token_end = save;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid_switchpoint)
|
||||||
{
|
{
|
||||||
pg_log_error("syntax error in history file: %s", fline);
|
pg_log_error("syntax error in history file: %s", fline);
|
||||||
pg_log_error_detail("Expected a write-ahead log switchpoint location.");
|
pg_log_error_detail("Expected a write-ahead log switchpoint location.");
|
||||||
@@ -96,7 +117,7 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
|
|||||||
entry = &entries[nlines - 1];
|
entry = &entries[nlines - 1];
|
||||||
entry->tli = tli;
|
entry->tli = tli;
|
||||||
entry->begin = prevend;
|
entry->begin = prevend;
|
||||||
entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
|
entry->end = switchpoint;
|
||||||
prevend = entry->end;
|
prevend = entry->end;
|
||||||
|
|
||||||
/* we ignore the remainder of each line */
|
/* we ignore the remainder of each line */
|
||||||
|
|||||||
Reference in New Issue
Block a user