You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2026-06-20 01:17:49 +02:00
Add repo-symlink option to suppress creation of repository symlinks.
Some otherwise Posix-compatible filesystems do not support symlinks. To support this allow symlinks to be disabled.
This commit is contained in:
@@ -25,6 +25,21 @@
|
||||
</release-item>
|
||||
</release-bug-list>
|
||||
|
||||
<release-feature-list>
|
||||
<release-item>
|
||||
<github-issue id="2673"/>
|
||||
<github-pull-request id="2674"/>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-ideator id="ron.johnson"/>
|
||||
<release-item-contributor id="david.steele"/>
|
||||
<release-item-reviewer id="douglas.j.hunley"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>Add <setting>repo-symlink</setting> option to suppress creation of repository symlinks.</p>
|
||||
</release-item>
|
||||
</release-feature-list>
|
||||
|
||||
<release-improvement-list>
|
||||
<release-item>
|
||||
<github-pull-request id="2661"/>
|
||||
|
||||
@@ -965,6 +965,11 @@
|
||||
<contributor-id type="github">rohitrav33ndran</contributor-id>
|
||||
</contributor>
|
||||
|
||||
<contributor id="ron.johnson">
|
||||
<contributor-name-display>Ron Johnson</contributor-name-display>
|
||||
<contributor-id type="github">RonJohn2</contributor-id>
|
||||
</contributor>
|
||||
|
||||
<contributor id="ronan.dunklau">
|
||||
<contributor-name-display>Ronan Dunklau</contributor-name-display>
|
||||
<contributor-id type="github">rdunklau</contributor-id>
|
||||
|
||||
@@ -2125,6 +2125,22 @@ option:
|
||||
required: false
|
||||
command: repo-type
|
||||
|
||||
repo-symlink:
|
||||
section: global
|
||||
group: repo
|
||||
type: boolean
|
||||
default: true
|
||||
command:
|
||||
backup: {}
|
||||
expire: {}
|
||||
command-role:
|
||||
main: {}
|
||||
depend:
|
||||
option: repo-type
|
||||
default: false
|
||||
list:
|
||||
- posix
|
||||
|
||||
repo-hardlink:
|
||||
section: global
|
||||
group: repo
|
||||
|
||||
@@ -1272,6 +1272,18 @@
|
||||
<example>n</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-symlink" name="Repository Symlink">
|
||||
<summary>Create symlinks within the repository.</summary>
|
||||
|
||||
<text>
|
||||
<p>Enable creation of the <file>lastest</file> and tablespace symlinks. These symlinks are most useful when using snapshots to do in-place recovery in the repository, which is an uncommon use case.</p>
|
||||
|
||||
<p>While this feature is likely not useful for the vast majority of users it remains on by default for legacy purposes. However, it may be useful to disable symlinks for Posix-like storage that does not support them.</p>
|
||||
</text>
|
||||
|
||||
<example>n</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-target-time" name="Target Time for Repository">
|
||||
<summary>Target time for repository.</summary>
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ Option constants
|
||||
#define CFGOPT_VERBOSE "verbose"
|
||||
#define CFGOPT_VERSION "version"
|
||||
|
||||
#define CFG_OPTION_TOTAL 187
|
||||
#define CFG_OPTION_TOTAL 188
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option value constants
|
||||
@@ -557,6 +557,7 @@ typedef enum
|
||||
cfgOptRepoStorageTag,
|
||||
cfgOptRepoStorageUploadChunkSize,
|
||||
cfgOptRepoStorageVerifyTls,
|
||||
cfgOptRepoSymlink,
|
||||
cfgOptRepoTargetTime,
|
||||
cfgOptRepoType,
|
||||
cfgOptReport,
|
||||
|
||||
@@ -9965,6 +9965,41 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
|
||||
), // opt/repo-storage-verify-tls
|
||||
), // opt/repo-storage-verify-tls
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-symlink
|
||||
( // opt/repo-symlink
|
||||
PARSE_RULE_OPTION_NAME("repo-symlink"), // opt/repo-symlink
|
||||
PARSE_RULE_OPTION_TYPE(Boolean), // opt/repo-symlink
|
||||
PARSE_RULE_OPTION_NEGATE(true), // opt/repo-symlink
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-symlink
|
||||
PARSE_RULE_OPTION_REQUIRED(true), // opt/repo-symlink
|
||||
PARSE_RULE_OPTION_SECTION(Global), // opt/repo-symlink
|
||||
PARSE_RULE_OPTION_GROUP_ID(Repo), // opt/repo-symlink
|
||||
// opt/repo-symlink
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-symlink
|
||||
( // opt/repo-symlink
|
||||
PARSE_RULE_OPTION_COMMAND(Backup) // opt/repo-symlink
|
||||
PARSE_RULE_OPTION_COMMAND(Expire) // opt/repo-symlink
|
||||
), // opt/repo-symlink
|
||||
// opt/repo-symlink
|
||||
PARSE_RULE_OPTIONAL // opt/repo-symlink
|
||||
( // opt/repo-symlink
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-symlink
|
||||
( // opt/repo-symlink
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-symlink
|
||||
( // opt/repo-symlink
|
||||
PARSE_RULE_OPTIONAL_DEPEND_DEFAULT(PARSE_RULE_VAL_BOOL_FALSE), // opt/repo-symlink
|
||||
PARSE_RULE_VAL_OPT(RepoType), // opt/repo-symlink
|
||||
PARSE_RULE_VAL_STRID(Posix), // opt/repo-symlink
|
||||
), // opt/repo-symlink
|
||||
// opt/repo-symlink
|
||||
PARSE_RULE_OPTIONAL_DEFAULT // opt/repo-symlink
|
||||
( // opt/repo-symlink
|
||||
PARSE_RULE_VAL_BOOL_TRUE, // opt/repo-symlink
|
||||
), // opt/repo-symlink
|
||||
), // opt/repo-symlink
|
||||
), // opt/repo-symlink
|
||||
), // opt/repo-symlink
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-target-time
|
||||
( // opt/repo-target-time
|
||||
PARSE_RULE_OPTION_NAME("repo-target-time"), // opt/repo-target-time
|
||||
@@ -11678,6 +11713,7 @@ static const uint8_t optionResolveOrder[] =
|
||||
cfgOptRepoStorageTag, // opt-resolve-order
|
||||
cfgOptRepoStorageUploadChunkSize, // opt-resolve-order
|
||||
cfgOptRepoStorageVerifyTls, // opt-resolve-order
|
||||
cfgOptRepoSymlink, // opt-resolve-order
|
||||
cfgOptTarget, // opt-resolve-order
|
||||
cfgOptTargetAction, // opt-resolve-order
|
||||
cfgOptTargetExclusive, // opt-resolve-order
|
||||
|
||||
@@ -24,5 +24,5 @@ storageCifsNew(
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(
|
||||
STORAGE, storagePosixNewInternal(STORAGE_CIFS_TYPE, path, modeFile, modePath, write, pathExpressionFunction, false));
|
||||
STORAGE, storagePosixNewInternal(STORAGE_CIFS_TYPE, path, modeFile, modePath, write, pathExpressionFunction, false, false));
|
||||
}
|
||||
|
||||
@@ -391,7 +391,8 @@ storageRepoGet(const unsigned int repoIdx, const bool write)
|
||||
CHECK(AssertError, type == STORAGE_POSIX_TYPE, "invalid storage type");
|
||||
|
||||
result = storagePosixNewP(
|
||||
cfgOptionIdxStr(cfgOptRepoPath, repoIdx), .write = write, .pathExpressionFunction = storageRepoPathExpression);
|
||||
cfgOptionIdxStr(cfgOptRepoPath, repoIdx), .write = write, .pathExpressionFunction = storageRepoPathExpression,
|
||||
.noSymLink = cfgOptionValid(cfgOptRepoSymlink) ? !cfgOptionIdxBool(cfgOptRepoSymlink, repoIdx) : true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -594,7 +594,7 @@ static const StorageInterface storageInterfacePosix =
|
||||
FN_EXTERN Storage *
|
||||
storagePosixNewInternal(
|
||||
const StringId type, const String *const path, const mode_t modeFile, const mode_t modePath, const bool write,
|
||||
StoragePathExpressionCallback pathExpressionFunction, const bool pathSync)
|
||||
StoragePathExpressionCallback pathExpressionFunction, const bool pathSync, const bool symLink)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STRING_ID, type);
|
||||
@@ -604,6 +604,7 @@ storagePosixNewInternal(
|
||||
FUNCTION_LOG_PARAM(BOOL, write);
|
||||
FUNCTION_LOG_PARAM(FUNCTIONP, pathExpressionFunction);
|
||||
FUNCTION_LOG_PARAM(BOOL, pathSync);
|
||||
FUNCTION_LOG_PARAM(BOOL, symLink);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(type != 0);
|
||||
@@ -628,9 +629,11 @@ storagePosixNewInternal(
|
||||
|
||||
// If this is a posix driver then add link features
|
||||
if (type == STORAGE_POSIX_TYPE)
|
||||
{
|
||||
this->interface.feature |=
|
||||
1 << storageFeatureHardLink | 1 << storageFeatureSymLink | 1 << storageFeaturePathSync |
|
||||
1 << storageFeatureHardLink | (unsigned int)symLink << storageFeatureSymLink | 1 << storageFeaturePathSync |
|
||||
1 << storageFeatureInfoDetail;
|
||||
}
|
||||
}
|
||||
OBJ_NEW_END();
|
||||
|
||||
@@ -646,6 +649,7 @@ storagePosixNew(const String *const path, const StoragePosixNewParam param)
|
||||
FUNCTION_LOG_PARAM(MODE, param.modeFile);
|
||||
FUNCTION_LOG_PARAM(MODE, param.modePath);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.write);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.noSymLink);
|
||||
FUNCTION_LOG_PARAM(FUNCTIONP, param.pathExpressionFunction);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
@@ -653,5 +657,6 @@ storagePosixNew(const String *const path, const StoragePosixNewParam param)
|
||||
STORAGE,
|
||||
storagePosixNewInternal(
|
||||
STORAGE_POSIX_TYPE, path, param.modeFile == 0 ? STORAGE_MODE_FILE_DEFAULT : param.modeFile,
|
||||
param.modePath == 0 ? STORAGE_MODE_PATH_DEFAULT : param.modePath, param.write, param.pathExpressionFunction, true));
|
||||
param.modePath == 0 ? STORAGE_MODE_PATH_DEFAULT : param.modePath, param.write, param.pathExpressionFunction, true,
|
||||
!param.noSymLink));
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef struct StoragePosixNewParam
|
||||
{
|
||||
VAR_PARAM_HEADER;
|
||||
bool write;
|
||||
bool noSymLink; // Do not create symlinks on this storage
|
||||
mode_t modeFile;
|
||||
mode_t modePath;
|
||||
StoragePathExpressionCallback *pathExpressionFunction;
|
||||
|
||||
@@ -16,7 +16,7 @@ Constructors
|
||||
***********************************************************************************************************************************/
|
||||
FN_EXTERN Storage *storagePosixNewInternal(
|
||||
StringId type, const String *path, mode_t modeFile, mode_t modePath, bool write,
|
||||
StoragePathExpressionCallback pathExpressionFunction, bool pathSync);
|
||||
StoragePathExpressionCallback pathExpressionFunction, bool pathSync, bool symLink);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for function logging
|
||||
|
||||
@@ -2918,6 +2918,7 @@ testRun(void)
|
||||
hrnCfgArgRaw(argList, cfgOptRepoPath, repoPath);
|
||||
hrnCfgArgRaw(argList, cfgOptPgPath, pg1Path);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoRetentionFull, "1");
|
||||
hrnCfgArgRawBool(argList, cfgOptRepoSymlink, false);
|
||||
hrnCfgArgRawStrId(argList, cfgOptType, backupTypeFull);
|
||||
hrnCfgArgRawBool(argList, cfgOptRepoHardlink, true);
|
||||
hrnCfgArgRawZ(argList, cfgOptManifestSaveThreshold, "1");
|
||||
@@ -2988,9 +2989,6 @@ testRun(void)
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||
// Disable storageFeatureSymLink so tablespace (and latest) symlinks will not be created
|
||||
((Storage *)storageRepoWrite())->pub.interface.feature ^= 1 << storageFeatureSymLink;
|
||||
|
||||
// Disable storageFeatureHardLink so hardlinks will not be created
|
||||
((Storage *)storageRepoWrite())->pub.interface.feature ^= 1 << storageFeatureHardLink;
|
||||
|
||||
@@ -2999,7 +2997,6 @@ testRun(void)
|
||||
TEST_RESULT_VOID(hrnCmdBackup(), "backup");
|
||||
|
||||
// Reset storage features
|
||||
((Storage *)storageRepoWrite())->pub.interface.feature |= 1 << storageFeatureSymLink;
|
||||
((Storage *)storageRepoWrite())->pub.interface.feature |= 1 << storageFeatureHardLink;
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
@@ -894,7 +894,7 @@ testRun(void)
|
||||
int invalidLinkType = 9;
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("create soft link to BACKUPLABEL");
|
||||
TEST_TITLE("soft link success/fail");
|
||||
|
||||
TEST_RESULT_VOID(storagePathCreateP(storageTest, backupLabel), "create path to link to");
|
||||
TEST_RESULT_VOID(storageLinkCreateP(storageTest, backupLabel, latestLabel), "create symlink");
|
||||
@@ -907,6 +907,14 @@ testRun(void)
|
||||
TEST_RESULT_STR(info.linkDestination, backupLabel, "match link destination");
|
||||
TEST_RESULT_VOID(storageRemoveP(storageTest, latestLabel), "remove symlink");
|
||||
|
||||
TEST_ERROR(
|
||||
storageLinkCreateP(
|
||||
storagePosixNewP(STRDEF("/"), .write = true, .noSymLink = true), backupLabel, latestLabel,
|
||||
.linkType = storageLinkSym),
|
||||
AssertError,
|
||||
"assertion '(param.linkType == storageLinkSym && storageFeature(this, storageFeatureSymLink)) ||"
|
||||
" (param.linkType == storageLinkHard && storageFeature(this, storageFeatureHardLink))' failed");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("hardlink success/fail");
|
||||
|
||||
@@ -1636,6 +1644,27 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(storageSpool(), AssertError, "stanza cannot be NULL for this storage object");
|
||||
TEST_ERROR(storageSpoolWrite(), AssertError, "stanza cannot be NULL for this storage object");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("enable/disable symlink");
|
||||
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
|
||||
hrnCfgArgRawZ(argList, cfgOptPgPath, TEST_PATH "/db");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, TEST_PATH "/repo1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoRetentionFull, 1, "1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, TEST_PATH "/repo2");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoRetentionFull, 2, "1");
|
||||
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
||||
|
||||
TEST_RESULT_BOOL(storageFeature(storageRepoIdxWrite(0), storageFeatureSymLink), true, "repo1 symlink enabled");
|
||||
TEST_RESULT_BOOL(storageFeature(storageRepoIdxWrite(1), storageFeatureSymLink), true, "repo2 symlink enabled");
|
||||
|
||||
hrnCfgArgKeyRawBool(argList, cfgOptRepoSymlink, 2, false);
|
||||
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
||||
|
||||
TEST_RESULT_BOOL(storageFeature(storageRepoIdxWrite(0), storageFeatureSymLink), true, "repo1 symlink enabled");
|
||||
TEST_RESULT_BOOL(storageFeature(storageRepoIdxWrite(1), storageFeatureSymLink), false, "repo2 symlink disabled");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
|
||||
Reference in New Issue
Block a user