1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-01-18 04:58:51 +02:00

Fix archive-push/archive-get when PGDATA is symlinked.

Commit 7168e074 tried to use cwd() as PGDATA but this would disagree with the path configured in pgBackRest if PGDATA was symlinked.

If cwd() does not match the pgBackRest path then chdir() to the path and make sure the next cwd() matches the result from the first call.
This commit is contained in:
David Steele 2019-12-11 14:36:39 -05:00
parent 8c840c28a6
commit 0194a98671
8 changed files with 100 additions and 15 deletions

View File

@ -15,6 +15,17 @@
<release date="XXXX-XX-XX" version="2.20dev" title="UNDER DEVELOPMENT">
<release-core-list>
<release-bug-list>
<release-item>
<release-item-contributor-list>
<release-item-ideator id="stephen.frost"/>
<release-item-ideator id="milosz.suchy"/>
</release-item-contributor-list>
<p>Fix archive-push/archive-get when <path>PGDATA</path> is symlinked.</p>
<p>These commands tried to use <code>cwd()</code> as <path>PGDATA</path> but this would disagree with the path configured in pgBackRest if <path>PGDATA</path> was symlinked. If <code>cwd()</code> does not match the <backrest/> path then <code>chdir()</code> to the path and make sure the next <code>cwd()</code> matches the result from the first call.</p>
</release-item>
<release-item>
<p>Fix reference list when <file>backup.info</file> is reconstructed in <cmd>expire</cmd> command.</p>
@ -7809,6 +7820,11 @@
<contributor-id type="github">M1hacka</contributor-id>
</contributor>
<contributor id="milosz.suchy">
<contributor-name-display>Milosz Suchy</contributor-name-display>
<contributor-id type="github">Yuxael</contributor-id>
</contributor>
<contributor id="mohamad.el.rifai">
<contributor-name-display>Mohamad El-Rifai</contributor-name-display>
<contributor-id type="github">melrifa1</contributor-id>

View File

@ -229,25 +229,56 @@ walIsPartial(const String *walSegment)
}
/***********************************************************************************************************************************
Generates the location of the wal directory using either an absolute path or cwd() and a relative path
Generates the location of the wal directory using a relative wal path and the supplied pg path
***********************************************************************************************************************************/
String *
walPath(const String *walFile)
walPath(const String *walFile, const String *pgPath, const String *command)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING, walFile);
FUNCTION_LOG_PARAM(STRING, pgPath);
FUNCTION_LOG_PARAM(STRING, command);
FUNCTION_LOG_END();
ASSERT(walFile != NULL);
ASSERT(command != NULL);
String *result = NULL;
if (!strBeginsWithZ(walFile, "/"))
{
char currentWorkDir[4096];
// Error if walFile has a relative path and pgPath is not set
if (pgPath == NULL)
{
THROW_FMT(
OptionRequiredError,
"option '" CFGOPT_PG1_PATH "' must be specified when relative wal paths are used\n"
"HINT: is %%f passed to %s instead of %%p?\n"
"HINT: PostgreSQL may pass relative paths even with %%p depending on the environment.",
strPtr(command));
}
// Get the working directory
char currentWorkDir[4096];
THROW_ON_SYS_ERROR(getcwd(currentWorkDir, sizeof(currentWorkDir)) == NULL, FormatError, "unable to get cwd");
result = strNewFmt("%s/%s", strcmp(currentWorkDir, "/") != 0 ? currentWorkDir : "", strPtr(walFile));
// Check if the working directory is the same as pgPath
if (!strEqZ(pgPath, currentWorkDir))
{
// If not we'll change the working directory to pgPath and see if that equals the working directory we got called with
THROW_ON_SYS_ERROR_FMT(chdir(strPtr(pgPath)) != 0, PathMissingError, "unable to chdir() to '%s'", strPtr(pgPath));
// Get the new working directory
char newWorkDir[4096];
THROW_ON_SYS_ERROR(getcwd(newWorkDir, sizeof(newWorkDir)) == NULL, FormatError, "unable to get cwd");
// Error if the new working directory is not equal to the original current working directory. This means that
// PostgreSQL and pgBackrest have a different idea about where the PostgreSQL data directory is located.
if (strcmp(currentWorkDir, newWorkDir) != 0)
THROW_FMT(AssertError, "working path '%s' is not the same path as '%s'", currentWorkDir, strPtr(pgPath));
}
result = strNewFmt("%s/%s", strPtr(pgPath), strPtr(walFile));
}
else
result = strDup(walFile);

View File

@ -64,7 +64,7 @@ void archiveAsyncStatusErrorWrite(ArchiveMode archiveMode, const String *walSegm
bool walIsPartial(const String *walSegment);
bool walIsSegment(const String *walSegment);
String *walPath(const String *walFile);
String *walPath(const String *walFile, const String *pgPath, const String *command);
String *walSegmentFind(const Storage *storage, const String *archiveId, const String *walSegment, TimeMSec timeout);
String *walSegmentNext(const String *walSegment, size_t walSegmentSize, unsigned int pgVersion);
StringList *walSegmentRange(const String *walSegmentBegin, size_t walSegmentSize, unsigned int pgVersion, unsigned int range);

View File

@ -132,7 +132,8 @@ cmdArchiveGet(void)
String *walSegment = strBase(strLstGet(commandParam, 0));
// Destination is wherever we were told to move the WAL segment
const String *walDestination = walPath(strLstGet(commandParam, 1));
const String *walDestination =
walPath(strLstGet(commandParam, 1), cfgOptionStr(cfgOptPgPath), STR(cfgCommandName(cfgCommand())));
// Async get can only be performed on WAL segments, history or other files must use synchronous mode
if (cfgOptionBool(cfgOptArchiveAsync) && walIsSegment(walSegment))

View File

@ -268,7 +268,7 @@ cmdArchivePush(void)
lockStopTest();
// Get the segment name
String *walFile = walPath(strLstGet(commandParam, 0));
String *walFile = walPath(strLstGet(commandParam, 0), cfgOptionStr(cfgOptPgPath), STR(cfgCommandName(cfgCommand())));
String *archiveFile = strBase(walFile);
if (cfgOptionBool(cfgOptArchiveAsync))

View File

@ -172,14 +172,39 @@ testRun(void)
// *****************************************************************************************************************************
if (testBegin("walPath()"))
{
THROW_ON_SYS_ERROR(chdir("/tmp") != 0, PathMissingError, "unable to chdir()");
const String *pgPath = storagePathP(storageTest, STRDEF("pg"));
storagePathCreateP(storageTest, pgPath);
TEST_RESULT_STR(
strPtr(walPath(strNew("/absolute/path"), pgPath, strNew("test"))), "/absolute/path", "absolute path");
THROW_ON_SYS_ERROR(chdir(strPtr(pgPath)) != 0, PathMissingError, "unable to chdir()");
TEST_RESULT_STR_STR(
walPath(strNew("relative/path"), pgPath, strNew("test")), strNewFmt("%s/relative/path", strPtr(pgPath)),
"relative path");
const String *pgPathLink = storagePathP(storageTest, STRDEF("pg-link"));
THROW_ON_SYS_ERROR_FMT(
symlink(strPtr(pgPath), strPtr(pgPathLink)) == -1, FileOpenError,
"unable to create symlink '%s' to '%s'", strPtr(pgPath), strPtr(pgPathLink));
THROW_ON_SYS_ERROR(chdir(strPtr(pgPath)) != 0, PathMissingError, "unable to chdir()");
TEST_RESULT_STR_STR(
walPath(strNew("relative/path"), pgPathLink, strNew("test")), strNewFmt("%s/relative/path", strPtr(pgPathLink)),
"relative path");
TEST_RESULT_STR(strPtr(walPath(strNew("/absolute/path"))), "/absolute/path", "absolute path");
TEST_RESULT_STR(strPtr(walPath(strNew("relative/path"))), "/tmp/relative/path", "relative path");
THROW_ON_SYS_ERROR(chdir("/") != 0, PathMissingError, "unable to chdir()");
TEST_ERROR(
walPath(strNew("relative/path"), pgPathLink, strNew("test")), AssertError,
hrnReplaceKey("working path '/' is not the same path as '{[path]}/pg-link'"));
TEST_RESULT_STR(strPtr(walPath(strNew("relative/path"))), "/relative/path", "relative path");
TEST_ERROR(
walPath(strNew("relative/path"), NULL, strNew("test")), OptionRequiredError,
"option 'pg1-path' must be specified when relative wal paths are used\n"
"HINT: is %f passed to test instead of %p?\n"
"HINT: PostgreSQL may pass relative paths even with %p depending on the environment.");
}
// *****************************************************************************************************************************

View File

@ -588,6 +588,8 @@ testRun(void)
strLstAdd(argList, strNewFmt("--pg1-path=%s/db", testPath()));
harnessCfgLoadRaw(strLstSize(argList), strLstPtr(argList));
THROW_ON_SYS_ERROR(chdir(strPtr(cfgOptionStr(cfgOptPgPath))) != 0, PathMissingError, "unable to chdir()");
HARNESS_FORK_BEGIN()
{
HARNESS_FORK_CHILD_BEGIN(0, false)
@ -623,8 +625,6 @@ testRun(void)
// Write out a WAL segment for success
// -------------------------------------------------------------------------------------------------------------------------
THROW_ON_SYS_ERROR(chdir(strPtr(cfgOptionStr(cfgOptPgPath))) != 0, PathMissingError, "unable to chdir()");
storagePutP(
storageNewWriteP(storageSpoolWrite(), strNewFmt(STORAGE_SPOOL_ARCHIVE_IN "/%s", strPtr(walSegment))),
BUFSTRDEF("SHOULD-BE-A-REAL-WAL-FILE"));

View File

@ -181,12 +181,23 @@ testRun(void)
TEST_ERROR(cmdArchivePush(), ParamRequiredError, "WAL segment to push required");
// -------------------------------------------------------------------------------------------------------------------------
StringList *argListTemp = strLstDup(argList);
strLstAddZ(argListTemp, "pg_wal/000000010000000100000001");
harnessCfgLoad(cfgCmdArchivePush, argListTemp);
TEST_ERROR(
cmdArchivePush(), OptionRequiredError,
"option 'pg1-path' must be specified when relative wal paths are used"
"\nHINT: is %f passed to archive-push instead of %p?"
"\nHINT: PostgreSQL may pass relative paths even with %p depending on the environment.");
// Create pg_control and archive.info
// -------------------------------------------------------------------------------------------------------------------------
strLstAdd(argList, strNewFmt("--pg1-path=%s/pg", testPath()));
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
StringList *argListTemp = strLstDup(argList);
argListTemp = strLstDup(argList);
strLstAddZ(argListTemp, "pg_wal/000000010000000100000001");
harnessCfgLoad(cfgCmdArchivePush, argListTemp);
@ -415,7 +426,8 @@ testRun(void)
strLstAddZ(argList, "pg_wal/bogus");
harnessCfgLoadRaw(strLstSize(argList), strLstPtr(argList));
THROW_ON_SYS_ERROR(chdir(testPath()) != 0, PathMissingError, "unable to chdir()");
storagePathCreateP(storageTest, cfgOptionStr(cfgOptPgPath));
THROW_ON_SYS_ERROR(chdir(strPtr(cfgOptionStr(cfgOptPgPath))) != 0, PathMissingError, "unable to chdir()");
TEST_ERROR(
cmdArchivePush(), ArchiveTimeoutError,