Skip to content

Commit 30b5ede

Browse files
committed
Fix escaping in generated recovery.conf file.
In the primary_conninfo line that "pg_basebackup -R" generates, single quotes in parameter values need to be escaped into \\'; the libpq parser requires the quotes to be escaped into \', and recovery.conf parser requires the \ to be escaped into \\. Also, don't quote parameter values unnecessarily, to make the connection string prettier. Most options in a libpq connection string don't need quoting. Reported by Hari Babu, closer analysis by Zoltan Boszormenyi, although I didn't use his patch.
1 parent 2af0971 commit 30b5ede

File tree

1 file changed

+89
-12
lines changed

1 file changed

+89
-12
lines changed

src/bin/pg_basebackup/pg_basebackup.c

Lines changed: 89 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,71 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum)
11071107
}
11081108

11091109
/*
1110-
* Escape single quotes used in connection parameters
1110+
* Escape a parameter value so that it can be used as part of a libpq
1111+
* connection string, e.g. in:
1112+
*
1113+
* application_name=<value>
1114+
*
1115+
* The returned string is malloc'd. Return NULL on out-of-memory.
1116+
*/
1117+
static char *
1118+
escapeConnectionParameter(const char *src)
1119+
{
1120+
bool need_quotes = false;
1121+
bool need_escaping = false;
1122+
const char *p;
1123+
char *dstbuf;
1124+
char *dst;
1125+
1126+
/*
1127+
* First check if quoting is needed. Any quote (') or backslash (\)
1128+
* characters need to be escaped. Parameters are separated by whitespace,
1129+
* so any string containing whitespace characters need to be quoted. An
1130+
* empty string is represented by ''.
1131+
*/
1132+
if (strchr(src, '\'') != NULL || strchr(src, '\\') != NULL)
1133+
need_escaping = true;
1134+
1135+
for (p = src; *p; p++)
1136+
{
1137+
if (isspace(*p))
1138+
{
1139+
need_quotes = true;
1140+
break;
1141+
}
1142+
}
1143+
1144+
if (*src == '\0')
1145+
return pg_strdup("''");
1146+
1147+
if (!need_quotes && !need_escaping)
1148+
return pg_strdup(src); /* no quoting or escaping needed */
1149+
1150+
/*
1151+
* Allocate a buffer large enough for the worst case that all the source
1152+
* characters need to be escaped, plus quotes.
1153+
*/
1154+
dstbuf = pg_malloc(strlen(src) * 2 + 2 + 1);
1155+
1156+
dst = dstbuf;
1157+
if (need_quotes)
1158+
*(dst++) = '\'';
1159+
for (; *src; src++)
1160+
{
1161+
if (*src == '\'' || *src == '\\')
1162+
*(dst++) = '\\';
1163+
*(dst++) = *src;
1164+
}
1165+
if (need_quotes)
1166+
*(dst++) = '\'';
1167+
*dst = '\0';
1168+
1169+
return dstbuf;
1170+
}
1171+
1172+
/*
1173+
* Escape a string so that it can be used as a value in a key-value pair
1174+
* a configuration file.
11111175
*/
11121176
static char *
11131177
escape_quotes(const char *src)
@@ -1130,6 +1194,8 @@ GenerateRecoveryConf(PGconn *conn)
11301194
{
11311195
PQconninfoOption *connOptions;
11321196
PQconninfoOption *option;
1197+
PQExpBufferData conninfo_buf;
1198+
char *escaped;
11331199

11341200
recoveryconfcontents = createPQExpBuffer();
11351201
if (!recoveryconfcontents)
@@ -1146,12 +1212,10 @@ GenerateRecoveryConf(PGconn *conn)
11461212
}
11471213

11481214
appendPQExpBufferStr(recoveryconfcontents, "standby_mode = 'on'\n");
1149-
appendPQExpBufferStr(recoveryconfcontents, "primary_conninfo = '");
11501215

1216+
initPQExpBuffer(&conninfo_buf);
11511217
for (option = connOptions; option && option->keyword; option++)
11521218
{
1153-
char *escaped;
1154-
11551219
/*
11561220
* Do not emit this setting if: - the setting is "replication",
11571221
* "dbname" or "fallback_application_name", since these would be
@@ -1165,24 +1229,37 @@ GenerateRecoveryConf(PGconn *conn)
11651229
(option->val != NULL && option->val[0] == '\0'))
11661230
continue;
11671231

1232+
/* Separate key-value pairs with spaces */
1233+
if (conninfo_buf.len != 0)
1234+
appendPQExpBufferStr(&conninfo_buf, " ");
1235+
11681236
/*
1169-
* Write "keyword='value'" pieces, the value string is escaped if
1170-
* necessary and doubled single quotes around the value string.
1237+
* Write "keyword=value" pieces, the value string is escaped and/or
1238+
* quoted if necessary.
11711239
*/
1172-
escaped = escape_quotes(option->val);
1173-
1174-
appendPQExpBuffer(recoveryconfcontents, "%s=''%s'' ", option->keyword, escaped);
1175-
1240+
escaped = escapeConnectionParameter(option->val);
1241+
appendPQExpBuffer(&conninfo_buf, "%s=%s", option->keyword, escaped);
11761242
free(escaped);
11771243
}
11781244

1179-
appendPQExpBufferStr(recoveryconfcontents, "'\n");
1180-
if (PQExpBufferBroken(recoveryconfcontents))
1245+
/*
1246+
* Escape the connection string, so that it can be put in the config file.
1247+
* Note that this is different from the escaping of individual connection
1248+
* options above!
1249+
*/
1250+
escaped = escape_quotes(conninfo_buf.data);
1251+
appendPQExpBuffer(recoveryconfcontents, "primary_conninfo = '%s'\n", escaped);
1252+
free(escaped);
1253+
1254+
if (PQExpBufferBroken(recoveryconfcontents) ||
1255+
PQExpBufferDataBroken(conninfo_buf))
11811256
{
11821257
fprintf(stderr, _("%s: out of memory\n"), progname);
11831258
disconnect_and_exit(1);
11841259
}
11851260

1261+
termPQExpBuffer(&conninfo_buf);
1262+
11861263
PQconninfoFree(connOptions);
11871264
}
11881265

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy