From 52987da056ffe14747ed2d9a5f2141bfb1069ad9 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 29 Jan 2026 11:00:06 +0300 Subject: [PATCH 01/21] repo-push command template --- src/Makefile.in | 1 + src/build/config/config.yaml | 20 +++++++- src/build/help/help.xml | 21 ++++++++ src/command/repo/push.c | 92 ++++++++++++++++++++++++++++++++++ src/command/repo/push.h | 13 +++++ src/config/config.auto.h | 4 +- src/config/parse.auto.c.inc | 45 +++++++++++++++++ src/main.c | 7 +++ src/meson.build | 1 + test/code-count/file-type.yaml | 8 +++ 10 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 src/command/repo/push.c create mode 100644 src/command/repo/push.h diff --git a/src/Makefile.in b/src/Makefile.in index 83d8798cdb..a2d3376c15 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -83,6 +83,7 @@ SRCS = \ command/repo/common.c \ command/repo/get.c \ command/repo/ls.c \ + command/repo/push.c \ command/repo/put.c \ command/repo/rm.c \ command/restore/blockChecksum.c \ diff --git a/src/build/config/config.yaml b/src/build/config/config.yaml index 07c7b2948b..f92684a21f 100644 --- a/src/build/config/config.yaml +++ b/src/build/config/config.yaml @@ -107,6 +107,14 @@ command: log-level-default: DEBUG parameter-allowed: true + repo-push: + command-role: + remote: {} + internal: true + log-file: false + log-level-default: DEBUG + parameter-allowed: true + repo-put: command-role: remote: {} @@ -392,6 +400,8 @@ option: manifest: default: latest required: true + repo-push: + required: true restore: default: latest required: true @@ -425,6 +435,8 @@ option: required: false repo-ls: required: false + repo-push: + required: false repo-put: required: false repo-rm: @@ -572,6 +584,7 @@ option: manifest: {} repo-get: {} repo-ls: {} + repo-push: {} repo-put: {} repo-rm: {} restore: {} @@ -614,6 +627,7 @@ option: manifest: {} repo-get: {} repo-ls: {} + repo-push: {} repo-put: {} repo-rm: {} restore: {} @@ -633,7 +647,7 @@ option: required: false command: repo-get: {} - repo-put: {} + repo-put: {} command-role: main: {} @@ -785,6 +799,7 @@ option: command: archive-push: {} backup: {} + repo-push: {} command-role: main: {} @@ -1731,6 +1746,9 @@ option: command-role: main: {} remote: {} + repo-push: + command-role: + main: {} repo-put: command-role: main: {} diff --git a/src/build/help/help.xml b/src/build/help/help.xml index a55b64e06a..4150623d20 100644 --- a/src/build/help/help.xml +++ b/src/build/help/help.xml @@ -2414,6 +2414,27 @@ + + Upload a metadata file to the repository. + + +

Upload a metadata file to the repository. The metadata file is stored in the same storage as the restore point and inherits the compression settings defined in the pgBackRest configuration.

+
+ + + + + +
+ Put a file in the repository. diff --git a/src/command/repo/push.c b/src/command/repo/push.c new file mode 100644 index 0000000000..c6c3946e63 --- /dev/null +++ b/src/command/repo/push.c @@ -0,0 +1,92 @@ +/*********************************************************************************************************************************** +Repository Put Command +***********************************************************************************************************************************/ +#include "build.auto.h" + +#include + +#include "command/repo/common.h" +#include "common/crypto/cipherBlock.h" +#include "common/debug.h" +#include "common/io/fdRead.h" +#include "common/io/io.h" +#include "common/log.h" +#include "common/memContext.h" +#include "config/config.h" +#include "storage/helper.h" + +/*********************************************************************************************************************************** +Write source IO to destination file +***********************************************************************************************************************************/ +static void +storagePushProcess(IoRead *source) +{ + FUNCTION_LOG_BEGIN(logLevelDebug); + FUNCTION_LOG_PARAM(IO_READ, source); + FUNCTION_LOG_END(); + + // Get destination file + const String *file = NULL; + + if (strLstSize(cfgCommandParam()) == 1) + file = strLstGet(cfgCommandParam(), 0); + else + THROW(ParamRequiredError, "metadata file required"); + + MEM_CONTEXT_TEMP_BEGIN() + { + // Is path valid for repo? + file = repoPathIsValid(file); + + StorageWrite *const destination = storageNewWriteP(storageRepoWrite(), file); + + // Add encryption if needed + if (!cfgOptionBool(cfgOptRaw)) + { + const CipherType repoCipherType = cfgOptionStrId(cfgOptRepoCipherType); + + if (repoCipherType != cipherTypeNone) + { + // Check for a passphrase parameter + const String *cipherPass = cfgOptionStrNull(cfgOptCipherPass); + + // If not passed as a parameter use the repo passphrase + if (cipherPass == NULL) + cipherPass = cfgOptionStr(cfgOptRepoCipherPass); + + // Add encryption filter + cipherBlockFilterGroupAdd( + ioWriteFilterGroup(storageWriteIo(destination)), repoCipherType, cipherModeEncrypt, cipherPass); + } + } + + // Open source and destination + ioReadOpen(source); + ioWriteOpen(storageWriteIo(destination)); + + // Copy data from source to destination + ioCopyP(source, storageWriteIo(destination)); + + // Close the source and destination + ioReadClose(source); + ioWriteClose(storageWriteIo(destination)); + } + MEM_CONTEXT_TEMP_END(); + + FUNCTION_LOG_RETURN_VOID(); +} + +/**********************************************************************************************************************************/ +FN_EXTERN void +cmdStoragePush(void) +{ + FUNCTION_LOG_VOID(logLevelDebug); + + MEM_CONTEXT_TEMP_BEGIN() + { + storagePushProcess(ioFdReadNew(STRDEF("stdin"), STDIN_FILENO, ioTimeoutMs())); + } + MEM_CONTEXT_TEMP_END(); + + FUNCTION_LOG_RETURN_VOID(); +} diff --git a/src/command/repo/push.h b/src/command/repo/push.h new file mode 100644 index 0000000000..1afb2dc683 --- /dev/null +++ b/src/command/repo/push.h @@ -0,0 +1,13 @@ +/*********************************************************************************************************************************** +Repository Push Command +***********************************************************************************************************************************/ +#ifndef COMMAND_STORAGE_PUSH_H +#define COMMAND_STORAGE_PUSH_H + +/*********************************************************************************************************************************** +Functions +***********************************************************************************************************************************/ +// Push a metadata file into the repository +FN_EXTERN void cmdStoragePush(void); + +#endif diff --git a/src/config/config.auto.h b/src/config/config.auto.h index 72067a3876..b36e2d4e88 100644 --- a/src/config/config.auto.h +++ b/src/config/config.auto.h @@ -20,6 +20,7 @@ Command constants #define CFGCMD_MANIFEST "manifest" #define CFGCMD_REPO_GET "repo-get" #define CFGCMD_REPO_LS "repo-ls" +#define CFGCMD_REPO_PUSH "repo-push" #define CFGCMD_REPO_PUT "repo-put" #define CFGCMD_REPO_RM "repo-rm" #define CFGCMD_RESTORE "restore" @@ -33,7 +34,7 @@ Command constants #define CFGCMD_VERIFY "verify" #define CFGCMD_VERSION "version" -#define CFG_COMMAND_TOTAL 23 +#define CFG_COMMAND_TOTAL 24 /*********************************************************************************************************************************** Option group constants @@ -371,6 +372,7 @@ typedef enum cfgCmdManifest, cfgCmdRepoGet, cfgCmdRepoLs, + cfgCmdRepoPush, cfgCmdRepoPut, cfgCmdRepoRm, cfgCmdRestore, diff --git a/src/config/parse.auto.c.inc b/src/config/parse.auto.c.inc index edddfc6f70..5bc2c8615c 100644 --- a/src/config/parse.auto.c.inc +++ b/src/config/parse.auto.c.inc @@ -822,6 +822,20 @@ static const ParseRuleCommand parseRuleCommand[CFG_COMMAND_TOTAL] = ), // cmd/repo-ls ), // cmd/repo-ls // ----------------------------------------------------------------------------------------------------------------------------- + PARSE_RULE_COMMAND // cmd/repo-push + ( // cmd/repo-push + PARSE_RULE_COMMAND_NAME("repo-push"), // cmd/repo-push + PARSE_RULE_COMMAND_LOCK_TYPE(None), // cmd/repo-push + PARSE_RULE_COMMAND_LOG_LEVEL_DEFAULT(Debug), // cmd/repo-push + PARSE_RULE_COMMAND_PARAMETER_ALLOWED(true), // cmd/repo-push + // cmd/repo-push + PARSE_RULE_COMMAND_ROLE_VALID_LIST // cmd/repo-push + ( // cmd/repo-push + PARSE_RULE_COMMAND_ROLE(Main) // cmd/repo-push + PARSE_RULE_COMMAND_ROLE(Remote) // cmd/repo-push + ), // cmd/repo-push + ), // cmd/repo-push + // ----------------------------------------------------------------------------------------------------------------------------- PARSE_RULE_COMMAND // cmd/repo-put ( // cmd/repo-put PARSE_RULE_COMMAND_NAME("repo-put"), // cmd/repo-put @@ -1476,6 +1490,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/beta PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/beta PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/beta + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/beta PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/beta PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/beta PARSE_RULE_OPTION_COMMAND(Restore) // opt/beta @@ -1515,6 +1530,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/beta PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/beta PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/beta + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/beta PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/beta PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/beta PARSE_RULE_OPTION_COMMAND(Restore) // opt/beta @@ -1768,6 +1784,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/compress PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/compress PARSE_RULE_OPTION_COMMAND(Backup) // opt/compress + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/compress ), // opt/compress // opt/compress PARSE_RULE_OPTIONAL // opt/compress @@ -1794,6 +1811,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/compress-level PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/compress-level PARSE_RULE_OPTION_COMMAND(Backup) // opt/compress-level + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/compress-level ), // opt/compress-level // opt/compress-level PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/compress-level @@ -1874,6 +1892,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/compress-type PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/compress-type PARSE_RULE_OPTION_COMMAND(Backup) // opt/compress-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/compress-type ), // opt/compress-type // opt/compress-type PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/compress-type @@ -1928,6 +1947,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/config PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/config PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/config + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/config PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/config PARSE_RULE_OPTION_COMMAND(Restore) // opt/config @@ -1967,6 +1987,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/config PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/config PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/config + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/config PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/config PARSE_RULE_OPTION_COMMAND(Restore) // opt/config @@ -2007,6 +2028,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/config-include-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/config-include-path @@ -2046,6 +2068,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/config-include-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/config-include-path @@ -2086,6 +2109,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/config-path PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/config-path PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/config-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config-path PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/config-path PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/config-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/config-path @@ -2125,6 +2149,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/config-path PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/config-path PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/config-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config-path PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/config-path PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/config-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/config-path @@ -2330,6 +2355,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/exec-id PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/exec-id PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/exec-id + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/exec-id PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/exec-id PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/exec-id PARSE_RULE_OPTION_COMMAND(Restore) // opt/exec-id @@ -2369,6 +2395,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/exec-id PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/exec-id PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/exec-id + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/exec-id PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/exec-id PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/exec-id PARSE_RULE_OPTION_COMMAND(Restore) // opt/exec-id @@ -2942,6 +2969,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/log-level-console + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-level-console @@ -2981,6 +3009,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/log-level-console + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-level-console @@ -3033,6 +3062,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/log-level-file + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-level-file @@ -3072,6 +3102,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/log-level-file + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-level-file @@ -3124,6 +3155,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/log-level-stderr + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-level-stderr @@ -3163,6 +3195,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/log-level-stderr + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-level-stderr @@ -3215,6 +3248,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/log-path PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/log-path PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/log-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-path PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/log-path PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/log-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-path @@ -3254,6 +3288,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/log-path PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/log-path PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/log-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-path PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/log-path PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/log-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-path @@ -3372,6 +3407,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/log-timestamp + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-timestamp @@ -3411,6 +3447,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/log-timestamp + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-timestamp @@ -4618,6 +4655,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/process PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/process PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/process + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/process PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/process PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/process PARSE_RULE_OPTION_COMMAND(Restore) // opt/process @@ -4910,6 +4948,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/remote-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/remote-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/remote-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/remote-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/remote-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/remote-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/remote-type @@ -4949,6 +4988,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo @@ -9936,6 +9976,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Expire) // opt/set PARSE_RULE_OPTION_COMMAND(Info) // opt/set PARSE_RULE_OPTION_COMMAND(Manifest) // opt/set + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/set PARSE_RULE_OPTION_COMMAND(Restore) // opt/set PARSE_RULE_OPTION_COMMAND(Verify) // opt/set ), // opt/set @@ -9947,6 +9988,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_FILTER_CMD // opt/set ( // opt/set PARSE_RULE_VAL_CMD(Annotate), // opt/set + PARSE_RULE_VAL_CMD(RepoPush), // opt/set ), // opt/set // opt/set PARSE_RULE_OPTIONAL_REQUIRED(), // opt/set @@ -10093,6 +10135,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/stanza PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/stanza PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/stanza + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/stanza PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/stanza PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/stanza PARSE_RULE_OPTION_COMMAND(Restore) // opt/stanza @@ -10130,6 +10173,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/stanza PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/stanza PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/stanza + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/stanza PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/stanza PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/stanza PARSE_RULE_OPTION_COMMAND(Restore) // opt/stanza @@ -10149,6 +10193,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_VAL_CMD(Info), // opt/stanza PARSE_RULE_VAL_CMD(RepoGet), // opt/stanza PARSE_RULE_VAL_CMD(RepoLs), // opt/stanza + PARSE_RULE_VAL_CMD(RepoPush), // opt/stanza PARSE_RULE_VAL_CMD(RepoPut), // opt/stanza PARSE_RULE_VAL_CMD(RepoRm), // opt/stanza PARSE_RULE_VAL_CMD(Start), // opt/stanza diff --git a/src/main.c b/src/main.c index 1c1b4d54b7..a61613432c 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,7 @@ Main #include "command/remote/remote.h" #include "command/repo/get.h" #include "command/repo/ls.h" +#include "command/repo/push.h" #include "command/repo/put.h" #include "command/repo/rm.h" #include "command/restore/restore.h" @@ -199,6 +200,12 @@ main(int argListSize, const char *argList[]) cmdStorageList(); break; + // Repository push metadata file command + // ----------------------------------------------------------------------------------------------------------------- + case cfgCmdRepoPush: + cmdStoragePush(); + break; + // Repository put file command // ----------------------------------------------------------------------------------------------------------------- case cfgCmdRepoPut: diff --git a/src/meson.build b/src/meson.build index 8d2863af70..f4ac9beb32 100644 --- a/src/meson.build +++ b/src/meson.build @@ -151,6 +151,7 @@ src_pgbackrest = [ 'command/repo/common.c', 'command/repo/get.c', 'command/repo/ls.c', + 'command/repo/push.c', 'command/repo/put.c', 'command/repo/rm.c', 'command/restore/blockChecksum.c', diff --git a/test/code-count/file-type.yaml b/test/code-count/file-type.yaml index 538dfdc83e..eac6bc271a 100644 --- a/test/code-count/file-type.yaml +++ b/test/code-count/file-type.yaml @@ -1127,6 +1127,14 @@ src/command/repo/ls.h: class: core type: c/h +src/command/repo/push.c: + class: core + type: c + +src/command/repo/push.h: + class: core + type: c/h + src/command/repo/put.c: class: core type: c From 1e024b992b68a0cd786e9119015f18dc98724ae3 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Sun, 1 Feb 2026 21:37:49 +0300 Subject: [PATCH 02/21] File upload --- src/build/config/config.yaml | 7 +++ src/command/repo/push.c | 66 ++++++++++++++++---- src/config/parse.auto.c.inc | 113 +++++++++++++++++++++++++++++++++++ src/info/manifest.c | 2 + 4 files changed, 176 insertions(+), 12 deletions(-) diff --git a/src/build/config/config.yaml b/src/build/config/config.yaml index f92684a21f..47823db750 100644 --- a/src/build/config/config.yaml +++ b/src/build/config/config.yaml @@ -109,6 +109,8 @@ command: repo-push: command-role: + async: {} + local: {} remote: {} internal: true log-file: false @@ -677,6 +679,7 @@ option: default: false command: repo-get: {} + repo-push: {} repo-put: {} command-role: main: {} @@ -1869,6 +1872,10 @@ option: command-role: main: {} remote: {} + repo-push: + command-role: + main: {} + remote: {} repo-put: command-role: main: {} diff --git a/src/command/repo/push.c b/src/command/repo/push.c index c6c3946e63..f174def22e 100644 --- a/src/command/repo/push.c +++ b/src/command/repo/push.c @@ -11,34 +11,64 @@ Repository Put Command #include "common/io/fdRead.h" #include "common/io/io.h" #include "common/log.h" +#include "common/compress/helper.h" #include "common/memContext.h" #include "config/config.h" #include "storage/helper.h" +static String * +composeDestinationPath(const String *source) +{ + FUNCTION_LOG_BEGIN(logLevelDebug); + FUNCTION_LOG_PARAM(STRING, source); + FUNCTION_LOG_END(); + + FUNCTION_LOG_RETURN(STRING, strNewFmt("%s", strZ(source))); +} + /*********************************************************************************************************************************** Write source IO to destination file ***********************************************************************************************************************************/ static void -storagePushProcess(IoRead *source) +storagePushProcess(const String *file, CompressType compressType, int compressLevel) { FUNCTION_LOG_BEGIN(logLevelDebug); - FUNCTION_LOG_PARAM(IO_READ, source); + FUNCTION_LOG_PARAM(STRING, file); + FUNCTION_LOG_PARAM(ENUM, compressType); + FUNCTION_LOG_PARAM(INT, compressLevel); FUNCTION_LOG_END(); - // Get destination file - const String *file = NULL; + // Ensure that the file exists and readable + + // Normalize source file path + // Get current working dir + char currentWorkDir[1024]; + THROW_ON_SYS_ERROR(getcwd(currentWorkDir, sizeof(currentWorkDir)) == NULL, FormatError, "unable to get cwd"); + + // TODO: Use realpath() to normalize on posix. + + String *sourcePath = strPathAbsolute(file, strNewZ(currentWorkDir)); + + // Repository Path Formation - if (strLstSize(cfgCommandParam()) == 1) - file = strLstGet(cfgCommandParam(), 0); - else - THROW(ParamRequiredError, "metadata file required"); + String *destPath = composeDestinationPath(file); + + // Is path valid for repo? + destPath = repoPathIsValid(destPath); MEM_CONTEXT_TEMP_BEGIN() { - // Is path valid for repo? - file = repoPathIsValid(file); + StorageWrite *const destination = storageNewWriteP(storageRepoWrite(), destPath); + + IoRead *const source = storageReadIo(storageNewReadP(storageLocal(), sourcePath)); + + // Compression + + // See archive/push/push.c for compress example + + // Upload to Repository - StorageWrite *const destination = storageNewWriteP(storageRepoWrite(), file); + // Update manifest // Add encryption if needed if (!cfgOptionBool(cfgOptRaw)) @@ -84,7 +114,19 @@ cmdStoragePush(void) MEM_CONTEXT_TEMP_BEGIN() { - storagePushProcess(ioFdReadNew(STRDEF("stdin"), STDIN_FILENO, ioTimeoutMs())); + const StringList *params = cfgCommandParam(); + + if (strLstSize(params) != 1) + THROW(ParamInvalidError, "file parameter is required"); + + String *filename = strLstGet(cfgCommandParam(), 0); + + LOG_INFO_FMT( + "push file %s to the archive.", + strZ(filename)); + + storagePushProcess(filename, compressTypeEnum(cfgOptionStrId(cfgOptCompressType)), + cfgOptionInt(cfgOptCompressLevel)); } MEM_CONTEXT_TEMP_END(); diff --git a/src/config/parse.auto.c.inc b/src/config/parse.auto.c.inc index 5bc2c8615c..b21330d0a5 100644 --- a/src/config/parse.auto.c.inc +++ b/src/config/parse.auto.c.inc @@ -831,6 +831,8 @@ static const ParseRuleCommand parseRuleCommand[CFG_COMMAND_TOTAL] = // cmd/repo-push PARSE_RULE_COMMAND_ROLE_VALID_LIST // cmd/repo-push ( // cmd/repo-push + PARSE_RULE_COMMAND_ROLE(Async) // cmd/repo-push + PARSE_RULE_COMMAND_ROLE(Local) // cmd/repo-push PARSE_RULE_COMMAND_ROLE(Main) // cmd/repo-push PARSE_RULE_COMMAND_ROLE(Remote) // cmd/repo-push ), // cmd/repo-push @@ -1508,6 +1510,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/beta PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/beta PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/beta + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/beta ), // opt/beta // opt/beta PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/beta @@ -1515,6 +1518,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/beta PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/beta PARSE_RULE_OPTION_COMMAND(Backup) // opt/beta + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/beta PARSE_RULE_OPTION_COMMAND(Restore) // opt/beta PARSE_RULE_OPTION_COMMAND(Verify) // opt/beta ), // opt/beta @@ -1817,6 +1821,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/compress-level ( // opt/compress-level PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/compress-level + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/compress-level ), // opt/compress-level ), // opt/compress-level // ----------------------------------------------------------------------------------------------------------------------------- @@ -1898,6 +1903,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/compress-type ( // opt/compress-type PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/compress-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/compress-type ), // opt/compress-type // opt/compress-type PARSE_RULE_OPTIONAL // opt/compress-type @@ -1965,6 +1971,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/config PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/config PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/config + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config ), // opt/config // opt/config PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/config @@ -1972,6 +1979,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/config PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/config PARSE_RULE_OPTION_COMMAND(Backup) // opt/config + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config PARSE_RULE_OPTION_COMMAND(Restore) // opt/config PARSE_RULE_OPTION_COMMAND(Verify) // opt/config ), // opt/config @@ -2046,6 +2054,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/config-include-path PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/config-include-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config-include-path ), // opt/config-include-path // opt/config-include-path PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/config-include-path @@ -2053,6 +2062,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(Backup) // opt/config-include-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/config-include-path PARSE_RULE_OPTION_COMMAND(Verify) // opt/config-include-path ), // opt/config-include-path @@ -2127,6 +2137,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/config-path PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/config-path PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/config-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config-path ), // opt/config-path // opt/config-path PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/config-path @@ -2134,6 +2145,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/config-path PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/config-path PARSE_RULE_OPTION_COMMAND(Backup) // opt/config-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/config-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/config-path PARSE_RULE_OPTION_COMMAND(Verify) // opt/config-path ), // opt/config-path @@ -2373,6 +2385,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/exec-id PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/exec-id PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/exec-id + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/exec-id ), // opt/exec-id // opt/exec-id PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/exec-id @@ -2380,6 +2393,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/exec-id PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/exec-id PARSE_RULE_OPTION_COMMAND(Backup) // opt/exec-id + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/exec-id PARSE_RULE_OPTION_COMMAND(Restore) // opt/exec-id PARSE_RULE_OPTION_COMMAND(Verify) // opt/exec-id ), // opt/exec-id @@ -2987,6 +3001,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/log-level-console PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/log-level-console + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-console ), // opt/log-level-console // opt/log-level-console PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/log-level-console @@ -2994,6 +3009,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(Backup) // opt/log-level-console + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-level-console PARSE_RULE_OPTION_COMMAND(Verify) // opt/log-level-console ), // opt/log-level-console @@ -3080,6 +3096,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/log-level-file PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/log-level-file + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-file ), // opt/log-level-file // opt/log-level-file PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/log-level-file @@ -3087,6 +3104,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(Backup) // opt/log-level-file + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-level-file PARSE_RULE_OPTION_COMMAND(Verify) // opt/log-level-file ), // opt/log-level-file @@ -3173,6 +3191,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/log-level-stderr + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-stderr ), // opt/log-level-stderr // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/log-level-stderr @@ -3180,6 +3199,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(Backup) // opt/log-level-stderr + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-level-stderr PARSE_RULE_OPTION_COMMAND(Verify) // opt/log-level-stderr ), // opt/log-level-stderr @@ -3266,6 +3286,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/log-path PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/log-path PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/log-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-path ), // opt/log-path // opt/log-path PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/log-path @@ -3273,6 +3294,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/log-path PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/log-path PARSE_RULE_OPTION_COMMAND(Backup) // opt/log-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-path PARSE_RULE_OPTION_COMMAND(Verify) // opt/log-path ), // opt/log-path @@ -3425,6 +3447,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/log-timestamp + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-timestamp ), // opt/log-timestamp // opt/log-timestamp PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/log-timestamp @@ -3432,6 +3455,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(Backup) // opt/log-timestamp + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(Restore) // opt/log-timestamp PARSE_RULE_OPTION_COMMAND(Verify) // opt/log-timestamp ), // opt/log-timestamp @@ -4640,6 +4664,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/process PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/process PARSE_RULE_OPTION_COMMAND(Backup) // opt/process + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/process PARSE_RULE_OPTION_COMMAND(Restore) // opt/process PARSE_RULE_OPTION_COMMAND(Verify) // opt/process ), // opt/process @@ -4835,6 +4860,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/raw ( // opt/raw PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/raw + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/raw PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/raw ), // opt/raw // opt/raw @@ -4933,6 +4959,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/remote-type PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/remote-type PARSE_RULE_OPTION_COMMAND(Backup) // opt/remote-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/remote-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/remote-type PARSE_RULE_OPTION_COMMAND(Verify) // opt/remote-type ), // opt/remote-type @@ -5063,6 +5090,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-account PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-account PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-account + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-account PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-account PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-account PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-account @@ -5097,6 +5125,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-account PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-account PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-account + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-account PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-account PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-account PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-account @@ -5140,6 +5169,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-container PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-container PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-container + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-container PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-container PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-container PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-container @@ -5174,6 +5204,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-container PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-container PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-container + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-container PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-container PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-container PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-container @@ -5218,6 +5249,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-endpoint PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-endpoint PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-endpoint + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-endpoint PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-endpoint PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-endpoint PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-endpoint @@ -5252,6 +5284,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-endpoint PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-endpoint PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-endpoint + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-endpoint PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-endpoint PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-endpoint PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-endpoint @@ -5301,6 +5334,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-key PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-key PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-key + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-key PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-key PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-key PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-key @@ -5335,6 +5369,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-key PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-key PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-key + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-key PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-key PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-key PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-key @@ -5378,6 +5413,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-key-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-key-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-key-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-key-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-key-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-key-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-key-type @@ -5412,6 +5448,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-key-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-key-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-key-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-key-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-key-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-key-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-key-type @@ -5466,6 +5503,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-uri-style PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-uri-style PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-uri-style + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-uri-style PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-uri-style PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-uri-style PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-uri-style @@ -5500,6 +5538,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-azure-uri-style PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-azure-uri-style PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-azure-uri-style + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-azure-uri-style PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-azure-uri-style PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-azure-uri-style PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-azure-uri-style @@ -5853,6 +5892,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-cipher-pass PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-cipher-pass PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-cipher-pass + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-cipher-pass PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-cipher-pass PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-cipher-pass PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-cipher-pass @@ -5887,6 +5927,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-cipher-pass PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-cipher-pass PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-cipher-pass + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-cipher-pass PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-cipher-pass PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-cipher-pass PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-cipher-pass @@ -5931,6 +5972,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-cipher-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-cipher-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-cipher-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-cipher-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-cipher-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-cipher-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-cipher-type @@ -5965,6 +6007,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-cipher-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-cipher-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-cipher-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-cipher-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-cipher-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-cipher-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-cipher-type @@ -6013,6 +6056,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-gcs-bucket PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-gcs-bucket PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-gcs-bucket + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-gcs-bucket PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-gcs-bucket PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-gcs-bucket PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-gcs-bucket @@ -6047,6 +6091,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-gcs-bucket PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-gcs-bucket PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-gcs-bucket + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-gcs-bucket PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-gcs-bucket PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-gcs-bucket PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-gcs-bucket @@ -6089,6 +6134,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-gcs-endpoint PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-gcs-endpoint PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-gcs-endpoint + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-gcs-endpoint PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-gcs-endpoint PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-gcs-endpoint PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-gcs-endpoint @@ -6123,6 +6169,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-gcs-endpoint PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-gcs-endpoint PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-gcs-endpoint + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-gcs-endpoint PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-gcs-endpoint PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-gcs-endpoint PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-gcs-endpoint @@ -6171,6 +6218,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-gcs-key PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-gcs-key PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-gcs-key + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-gcs-key PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-gcs-key PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-gcs-key PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-gcs-key @@ -6205,6 +6253,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-gcs-key PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-gcs-key PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-gcs-key + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-gcs-key PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-gcs-key PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-gcs-key PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-gcs-key @@ -6249,6 +6298,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-gcs-key-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-gcs-key-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-gcs-key-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-gcs-key-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-gcs-key-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-gcs-key-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-gcs-key-type @@ -6283,6 +6333,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-gcs-key-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-gcs-key-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-gcs-key-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-gcs-key-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-gcs-key-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-gcs-key-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-gcs-key-type @@ -7140,6 +7191,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-path PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-path PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-path PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-path PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-path @@ -7174,6 +7226,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-path PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-path PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-path PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-path PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-path @@ -7396,6 +7449,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-bucket PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-bucket PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-bucket + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-bucket PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-bucket PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-bucket PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-bucket @@ -7430,6 +7484,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-bucket PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-bucket PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-bucket + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-bucket PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-bucket PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-bucket PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-bucket @@ -7474,6 +7529,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-endpoint PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-endpoint PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-endpoint + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-endpoint PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-endpoint PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-endpoint PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-endpoint @@ -7508,6 +7564,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-endpoint PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-endpoint PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-endpoint + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-endpoint PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-endpoint PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-endpoint PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-endpoint @@ -7553,6 +7610,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-key PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-key PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-key + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-key PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-key PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-key PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-key @@ -7587,6 +7645,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-key PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-key PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-key + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-key PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-key PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-key PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-key @@ -7632,6 +7691,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-key-secret PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-key-secret PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-key-secret + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-key-secret PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-key-secret PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-key-secret PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-key-secret @@ -7666,6 +7726,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-key-secret PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-key-secret PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-key-secret + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-key-secret PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-key-secret PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-key-secret PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-key-secret @@ -7709,6 +7770,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-key-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-key-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-key-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-key-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-key-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-key-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-key-type @@ -7743,6 +7805,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-key-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-key-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-key-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-key-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-key-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-key-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-key-type @@ -7798,6 +7861,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-kms-key-id PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-kms-key-id PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-kms-key-id + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-kms-key-id PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-kms-key-id PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-kms-key-id PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-kms-key-id @@ -7832,6 +7896,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-kms-key-id PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-kms-key-id PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-kms-key-id + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-kms-key-id PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-kms-key-id PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-kms-key-id PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-kms-key-id @@ -7876,6 +7941,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-region PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-region PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-region + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-region PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-region PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-region PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-region @@ -7910,6 +7976,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-region PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-region PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-region + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-region PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-region PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-region PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-region @@ -7953,6 +8020,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-role PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-role PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-role + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-role PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-role PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-role PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-role @@ -7987,6 +8055,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-role PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-role PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-role + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-role PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-role PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-role PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-role @@ -8031,6 +8100,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-sse-customer-key PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-sse-customer-key PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-sse-customer-key + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-sse-customer-key PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-sse-customer-key PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-sse-customer-key PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-sse-customer-key @@ -8065,6 +8135,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-sse-customer-key PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-sse-customer-key PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-sse-customer-key + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-sse-customer-key PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-sse-customer-key PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-sse-customer-key PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-sse-customer-key @@ -8109,6 +8180,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-token PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-token PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-token + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-token PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-token PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-token PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-token @@ -8143,6 +8215,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-token PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-token PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-token + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-token PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-token PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-token PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-token @@ -8186,6 +8259,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-uri-style PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-uri-style PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-uri-style + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-uri-style PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-uri-style PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-uri-style PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-uri-style @@ -8220,6 +8294,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-s3-uri-style PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-s3-uri-style PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-s3-uri-style + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-s3-uri-style PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-s3-uri-style PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-s3-uri-style PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-s3-uri-style @@ -8274,6 +8349,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host @@ -8308,6 +8384,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host @@ -8351,6 +8428,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host-fingerprint PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host-fingerprint PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host-fingerprint + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host-fingerprint PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host-fingerprint PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host-fingerprint PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host-fingerprint @@ -8385,6 +8463,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host-fingerprint PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host-fingerprint PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host-fingerprint + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host-fingerprint PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host-fingerprint PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host-fingerprint PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host-fingerprint @@ -8429,6 +8508,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host-key-check-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host-key-check-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host-key-check-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host-key-check-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host-key-check-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host-key-check-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host-key-check-type @@ -8463,6 +8543,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host-key-check-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host-key-check-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host-key-check-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host-key-check-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host-key-check-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host-key-check-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host-key-check-type @@ -8519,6 +8600,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host-key-hash-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host-key-hash-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host-key-hash-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host-key-hash-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host-key-hash-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host-key-hash-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host-key-hash-type @@ -8553,6 +8635,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host-key-hash-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host-key-hash-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host-key-hash-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host-key-hash-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host-key-hash-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host-key-hash-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host-key-hash-type @@ -8603,6 +8686,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host-port PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host-port PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host-port + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host-port PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host-port PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host-port PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host-port @@ -8637,6 +8721,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host-port PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host-port PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host-port + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host-port PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host-port PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host-port PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host-port @@ -8691,6 +8776,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host-user PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host-user PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host-user + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host-user PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host-user PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host-user PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host-user @@ -8725,6 +8811,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-host-user PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-host-user PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-host-user + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-host-user PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-host-user PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-host-user PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-host-user @@ -8769,6 +8856,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-known-host PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-known-host PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-known-host + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-known-host PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-known-host PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-known-host PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-known-host @@ -8803,6 +8891,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-known-host PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-known-host PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-known-host + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-known-host PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-known-host PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-known-host PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-known-host @@ -8847,6 +8936,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-private-key-file PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-private-key-file PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-private-key-file + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-private-key-file PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-private-key-file PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-private-key-file PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-private-key-file @@ -8881,6 +8971,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-private-key-file PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-private-key-file PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-private-key-file + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-private-key-file PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-private-key-file PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-private-key-file PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-private-key-file @@ -8925,6 +9016,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-private-key-passphrase PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-private-key-passphrase PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-private-key-passphrase + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-private-key-passphrase PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-private-key-passphrase PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-private-key-passphrase PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-private-key-passphrase @@ -8959,6 +9051,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-private-key-passphrase PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-private-key-passphrase PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-private-key-passphrase + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-private-key-passphrase PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-private-key-passphrase PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-private-key-passphrase PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-private-key-passphrase @@ -9002,6 +9095,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-public-key-file PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-public-key-file PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-public-key-file + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-public-key-file PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-public-key-file PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-public-key-file PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-public-key-file @@ -9036,6 +9130,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-sftp-public-key-file PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-sftp-public-key-file PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-sftp-public-key-file + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-sftp-public-key-file PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-sftp-public-key-file PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-sftp-public-key-file PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-sftp-public-key-file @@ -9079,6 +9174,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-ca-file PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-ca-file PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-ca-file + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-ca-file PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-ca-file PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-ca-file PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-ca-file @@ -9113,6 +9209,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-ca-file PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-ca-file PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-ca-file + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-ca-file PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-ca-file PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-ca-file PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-ca-file @@ -9158,6 +9255,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-ca-path PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-ca-path PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-ca-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-ca-path PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-ca-path PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-ca-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-ca-path @@ -9192,6 +9290,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-ca-path PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-ca-path PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-ca-path + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-ca-path PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-ca-path PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-ca-path PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-ca-path @@ -9237,6 +9336,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-host PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-host PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-host + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-host PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-host PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-host PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-host @@ -9271,6 +9371,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-host PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-host PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-host + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-host PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-host PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-host PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-host @@ -9315,6 +9416,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-port PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-port PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-port + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-port PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-port PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-port PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-port @@ -9349,6 +9451,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-port PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-port PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-port + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-port PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-port PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-port PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-port @@ -9405,6 +9508,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-tag PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-tag PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-tag + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-tag PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-tag PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-tag PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-tag @@ -9439,6 +9543,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-tag PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-tag PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-tag + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-tag PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-tag PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-tag PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-tag @@ -9484,6 +9589,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-upload-chunk-size PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-upload-chunk-size PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-upload-chunk-size + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-upload-chunk-size PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-upload-chunk-size PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-upload-chunk-size PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-upload-chunk-size @@ -9518,6 +9624,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-upload-chunk-size PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-upload-chunk-size PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-upload-chunk-size + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-upload-chunk-size PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-upload-chunk-size PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-upload-chunk-size PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-upload-chunk-size @@ -9570,6 +9677,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-verify-tls PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-verify-tls PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-verify-tls + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-verify-tls PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-verify-tls PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-verify-tls PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-verify-tls @@ -9604,6 +9712,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-storage-verify-tls PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-storage-verify-tls PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-storage-verify-tls + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-storage-verify-tls PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-storage-verify-tls PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-storage-verify-tls PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-storage-verify-tls @@ -9687,6 +9796,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-type @@ -9721,6 +9831,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/repo-type PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/repo-type PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/repo-type + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/repo-type PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/repo-type PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/repo-type PARSE_RULE_OPTION_COMMAND(Restore) // opt/repo-type @@ -10151,6 +10262,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/stanza PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/stanza PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/stanza + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/stanza ), // opt/stanza // opt/stanza PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/stanza @@ -10158,6 +10270,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/stanza PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/stanza PARSE_RULE_OPTION_COMMAND(Backup) // opt/stanza + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/stanza PARSE_RULE_OPTION_COMMAND(Restore) // opt/stanza PARSE_RULE_OPTION_COMMAND(Verify) // opt/stanza ), // opt/stanza diff --git a/src/info/manifest.c b/src/info/manifest.c index a11ffdecae..e865063e40 100644 --- a/src/info/manifest.c +++ b/src/info/manifest.c @@ -1867,6 +1867,8 @@ manifestBuildComplete( #define MANIFEST_SECTION_TARGET_PATH "target:path" #define MANIFEST_SECTION_TARGET_PATH_DEFAULT "target:path:default" +#define MANIFEST_SECTION_CUSTOM_FILE "custom:file" + #define MANIFEST_KEY_ANNOTATION "annotation" #define MANIFEST_KEY_BACKUP_ARCHIVE_START "backup-archive-start" #define MANIFEST_KEY_BACKUP_ARCHIVE_STOP "backup-archive-stop" From 1ea44116a2e2d7e9c2673ded8627ba1bb5c44b38 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Mon, 2 Feb 2026 19:16:20 +0300 Subject: [PATCH 03/21] Push file to the manifest --- src/build/config/config.yaml | 3 +- src/command/repo/push.c | 109 +++++++-- src/config/parse.auto.c.inc | 5 +- src/info/manifest.c | 416 ++++++++++++++++++++--------------- src/info/manifest.h | 24 ++ 5 files changed, 361 insertions(+), 196 deletions(-) diff --git a/src/build/config/config.yaml b/src/build/config/config.yaml index 47823db750..15d84757c3 100644 --- a/src/build/config/config.yaml +++ b/src/build/config/config.yaml @@ -338,6 +338,7 @@ option: backup: {} check: {} expire: {} + repo-push: {} restore: {} stanza-create: {} stanza-upgrade: {} @@ -438,7 +439,7 @@ option: repo-ls: required: false repo-push: - required: false + required: true repo-put: required: false repo-rm: diff --git a/src/command/repo/push.c b/src/command/repo/push.c index f174def22e..f494f51507 100644 --- a/src/command/repo/push.c +++ b/src/command/repo/push.c @@ -9,23 +9,28 @@ Repository Put Command #include "common/crypto/cipherBlock.h" #include "common/debug.h" #include "common/io/fdRead.h" +#include "common/io/filter/size.h" #include "common/io/io.h" #include "common/log.h" #include "common/compress/helper.h" #include "common/memContext.h" #include "config/config.h" #include "storage/helper.h" +#include "info/manifest.h" static String * -composeDestinationPath(const String *source) +composeDestinationPath(const String *source, const String *stanza, const String *backupLabel) { FUNCTION_LOG_BEGIN(logLevelDebug); FUNCTION_LOG_PARAM(STRING, source); FUNCTION_LOG_END(); - FUNCTION_LOG_RETURN(STRING, strNewFmt("%s", strZ(source))); + String *const result = strNewFmt("%s/%s/%s", strZ(stanza), strZ(backupLabel), strZ(source)); + + FUNCTION_LOG_RETURN(STRING, result); } + /*********************************************************************************************************************************** Write source IO to destination file ***********************************************************************************************************************************/ @@ -51,31 +56,49 @@ storagePushProcess(const String *file, CompressType compressType, int compressLe // Repository Path Formation - String *destPath = composeDestinationPath(file); + const String *stanza = cfgOptionStr(cfgOptStanza); + const String *backupLabel = cfgOptionStr(cfgOptSet); + + String *destPath = composeDestinationPath(file, stanza, backupLabel); // Is path valid for repo? destPath = repoPathIsValid(destPath); MEM_CONTEXT_TEMP_BEGIN() { - StorageWrite *const destination = storageNewWriteP(storageRepoWrite(), destPath); + bool repoChecksum = false; + const Storage *storage = storageRepoWrite(); + const StorageWrite *const destination = storageNewWriteP(storage, destPath); - IoRead *const source = storageReadIo(storageNewReadP(storageLocal(), sourcePath)); + IoFilterGroup *const filterGroup = ioWriteFilterGroup(storageWriteIo(destination)); + + CipherType cipherType = cfgOptionStrId(cfgOptRepoCipherType); + const String *cipherPass = cfgOptionStrNull(cfgOptRepoCipherPass); - // Compression + const String *manifestFileName = strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strZ(backupLabel)); + Manifest *manifest = manifestLoadFile( + storage, manifestFileName, + cipherType, cipherPass); - // See archive/push/push.c for compress example + // Add SHA1 filter + ioFilterGroupAdd(filterGroup, cryptoHashNew(hashTypeSha1)); - // Upload to Repository + // Add compression + if (compressType != compressTypeNone) + { + ioFilterGroupAdd( + ioWriteFilterGroup(storageWriteIo(destination)), + compressFilterP(compressType, cfgOptionInt(cfgOptCompressLevel))); - // Update manifest + repoChecksum = true; + } - // Add encryption if needed - if (!cfgOptionBool(cfgOptRaw)) + // Add encryption filter if required + if (manifestCipherSubPass(manifest) != NULL) { const CipherType repoCipherType = cfgOptionStrId(cfgOptRepoCipherType); - if (repoCipherType != cipherTypeNone) + if (repoCipherType != cipherTypeNone) { // Check for a passphrase parameter const String *cipherPass = cfgOptionStrNull(cfgOptCipherPass); @@ -84,12 +107,21 @@ storagePushProcess(const String *file, CompressType compressType, int compressLe if (cipherPass == NULL) cipherPass = cfgOptionStr(cfgOptRepoCipherPass); - // Add encryption filter - cipherBlockFilterGroupAdd( - ioWriteFilterGroup(storageWriteIo(destination)), repoCipherType, cipherModeEncrypt, cipherPass); - } + ioFilterGroupAdd( + ioWriteFilterGroup(storageWriteIo(destination)), + cipherBlockNewP( + cipherModeEncrypt, repoCipherType, BUFSTR(manifestCipherSubPass(manifest)) + ) + ); + repoChecksum = true; + } } + // Add size filter last to calculate repo size + ioFilterGroupAdd(filterGroup, ioSizeNew()); + + IoRead *const source = storageReadIo(storageNewReadP(storageLocal(), sourcePath)); + // Open source and destination ioReadOpen(source); ioWriteOpen(storageWriteIo(destination)); @@ -100,6 +132,42 @@ storagePushProcess(const String *file, CompressType compressType, int compressLe // Close the source and destination ioReadClose(source); ioWriteClose(storageWriteIo(destination)); + + // Use base path to set ownership and mode + const ManifestPath *const basePath = manifestPathFind(manifest, MANIFEST_TARGET_PGDATA_STR); + + // Add to manifest + uint64_t size = pckReadU64P(ioFilterGroupResultP(filterGroup, SIZE_FILTER_TYPE)); + ManifestFile customFile = + { + .name = destPath, + .mode = basePath->mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), + .user = basePath->user, + .group = basePath->group, + .size = size, + .sizeOriginal = size, + .sizeRepo = size, + .timestamp = time(NULL), + .checksumSha1 = bufPtr(pckReadBinP(ioFilterGroupResultP(filterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 0))), + }; + + if (repoChecksum) + { + PackRead * packRead = ioFilterGroupResultP(filterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 1); + ASSERT(packRead != NULL); + customFile.checksumRepoSha1 = bufPtr(pckReadBinP(packRead)); + } + + manifestCustomFileAdd(manifest, &customFile); + + // Save manifest + IoWrite *const manifestWrite = storageWriteIo( + storageNewWriteP( + storageRepoWrite(), + manifestFileName + )); + + manifestSave(manifest, manifestWrite); } MEM_CONTEXT_TEMP_END(); @@ -125,8 +193,13 @@ cmdStoragePush(void) "push file %s to the archive.", strZ(filename)); - storagePushProcess(filename, compressTypeEnum(cfgOptionStrId(cfgOptCompressType)), - cfgOptionInt(cfgOptCompressLevel)); + CompressType compressType = compressTypeNone; + if (cfgOptionValid(cfgOptCompress)) + { + compressType = compressTypeEnum(cfgOptionStrId(cfgOptCompressType)); + } + storagePushProcess(filename, compressType, + cfgOptionInt(cfgOptCompressLevel)); } MEM_CONTEXT_TEMP_END(); diff --git a/src/config/parse.auto.c.inc b/src/config/parse.auto.c.inc index b21330d0a5..3ea358c2a3 100644 --- a/src/config/parse.auto.c.inc +++ b/src/config/parse.auto.c.inc @@ -2536,6 +2536,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Backup) // opt/fork PARSE_RULE_OPTION_COMMAND(Check) // opt/fork PARSE_RULE_OPTION_COMMAND(Expire) // opt/fork + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/fork PARSE_RULE_OPTION_COMMAND(Restore) // opt/fork PARSE_RULE_OPTION_COMMAND(StanzaCreate) // opt/fork PARSE_RULE_OPTION_COMMAND(StanzaUpgrade) // opt/fork @@ -2546,6 +2547,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/fork PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/fork PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/fork + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/fork ), // opt/fork // opt/fork PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/fork @@ -2553,6 +2555,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/fork PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/fork PARSE_RULE_OPTION_COMMAND(Backup) // opt/fork + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/fork PARSE_RULE_OPTION_COMMAND(Restore) // opt/fork PARSE_RULE_OPTION_COMMAND(Verify) // opt/fork ), // opt/fork @@ -2564,6 +2567,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/fork PARSE_RULE_OPTION_COMMAND(Backup) // opt/fork PARSE_RULE_OPTION_COMMAND(Check) // opt/fork + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/fork PARSE_RULE_OPTION_COMMAND(Restore) // opt/fork PARSE_RULE_OPTION_COMMAND(StanzaCreate) // opt/fork PARSE_RULE_OPTION_COMMAND(StanzaUpgrade) // opt/fork @@ -10306,7 +10310,6 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_VAL_CMD(Info), // opt/stanza PARSE_RULE_VAL_CMD(RepoGet), // opt/stanza PARSE_RULE_VAL_CMD(RepoLs), // opt/stanza - PARSE_RULE_VAL_CMD(RepoPush), // opt/stanza PARSE_RULE_VAL_CMD(RepoPut), // opt/stanza PARSE_RULE_VAL_CMD(RepoRm), // opt/stanza PARSE_RULE_VAL_CMD(Start), // opt/stanza diff --git a/src/info/manifest.c b/src/info/manifest.c index e865063e40..fb0bd1825b 100644 --- a/src/info/manifest.c +++ b/src/info/manifest.c @@ -419,6 +419,30 @@ manifestFileAdd(Manifest *const this, ManifestFile *const file) FUNCTION_TEST_RETURN_VOID(); } +FN_EXTERN void +manifestCustomFileAdd(Manifest *const this, ManifestFile *const file) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(MANIFEST, this); + FUNCTION_TEST_PARAM(MANIFEST_FILE, file); + FUNCTION_TEST_END(); + + ASSERT(this != NULL); + ASSERT(file->name != NULL); + + file->user = manifestOwnerCache(this, file->user); + file->group = manifestOwnerCache(this, file->group); + + MEM_CONTEXT_BEGIN(lstMemContext(this->pub.customFileList)) + { + const ManifestFilePack *const filePack = manifestFilePack(this, file); + lstAdd(this->pub.customFileList, &filePack); + } + MEM_CONTEXT_END(); + + FUNCTION_TEST_RETURN_VOID(); +} + // Update file pack by creating a new one and then freeing the old one static void manifestFilePackUpdate(Manifest *const this, ManifestFilePack **const filePack, const ManifestFile *const file) @@ -557,6 +581,7 @@ manifestNewInternal(void) .linkList = lstNewP(sizeof(ManifestLink), .comparator = lstComparatorStr), .pathList = lstNewP(sizeof(ManifestPath), .comparator = lstComparatorStr), .targetList = lstNewP(sizeof(ManifestTarget), .comparator = lstComparatorStr), + .customFileList = lstNewP(sizeof(ManifestFilePack *), .comparator = lstComparatorStr), .referenceList = strLstNew(), }, .ownerList = strLstNew(), @@ -1995,142 +2020,156 @@ manifestOwnerDefaultGet(const Variant *const ownerDefault) FUNCTION_TEST_RETURN_CONST(VARIANT, varDup(ownerDefault)); } -static void -manifestLoadCallback(void *const callbackData, const String *const section, const String *const key, const String *const value) +static void manifestLoadFileEntry(Manifest *const manifest, ManifestLoadData *const loadData, const String *const key, const String *const value, ManifestFile *file) { - FUNCTION_TEST_BEGIN(); - FUNCTION_TEST_PARAM_P(VOID, callbackData); - FUNCTION_TEST_PARAM(STRING, section); - FUNCTION_TEST_PARAM(STRING, key); - FUNCTION_TEST_PARAM(STRING, value); - FUNCTION_TEST_END(); + JsonRead *const json = jsonReadNew(value); + jsonReadObjectBegin(json); - FUNCTION_AUDIT_CALLBACK(); + // Block incremental info + if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_BLOCK_INCR)) + { + file->blockIncrSize = (size_t)jsonReadUInt64(json) * BLOCK_INCR_SIZE_FACTOR; - ASSERT(callbackData != NULL); - ASSERT(section != NULL); - ASSERT(key != NULL); + if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_BLOCK_INCR_CHECKSUM)) + file->blockIncrChecksumSize = (size_t)jsonReadUInt64(json); + else + file->blockIncrChecksumSize = BLOCK_INCR_CHECKSUM_SIZE_MIN; - ManifestLoadData *const loadData = (ManifestLoadData *)callbackData; - Manifest *const manifest = loadData->manifest; + if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_BLOCK_INCR_MAP)) + file->blockIncrMapSize = jsonReadUInt64(json); + } - // ----------------------------------------------------------------------------------------------------------------------------- - if (strEqZ(section, MANIFEST_SECTION_TARGET_FILE)) + // Bundle info + if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_BUNDLE_ID)) { - ManifestFile file = {.name = key}; + file->bundleId = jsonReadUInt64(json); - JsonRead *const json = jsonReadNew(value); - jsonReadObjectBegin(json); + if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_BUNDLE_OFFSET)) + file->bundleOffset = jsonReadUInt64(json); + } - // Block incremental info - if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_BLOCK_INCR)) - { - file.blockIncrSize = (size_t)jsonReadUInt64(json) * BLOCK_INCR_SIZE_FACTOR; + // The checksum might not exist if this is a partial save that was done during the backup to preserve checksums for already + // backed up files + if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_CHECKSUM)) + file->checksumSha1 = bufPtr(bufNewDecode(encodingHex, jsonReadStr(json))); - if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_BLOCK_INCR_CHECKSUM)) - file.blockIncrChecksumSize = (size_t)jsonReadUInt64(json); - else - file.blockIncrChecksumSize = BLOCK_INCR_CHECKSUM_SIZE_MIN; + // Page checksum errors + if (jsonReadKeyExpectZ(json, MANIFEST_KEY_CHECKSUM_PAGE)) + { + file->checksumPage = true; + file->checksumPageError = !jsonReadBool(json); - if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_BLOCK_INCR_MAP)) - file.blockIncrMapSize = jsonReadUInt64(json); - } + if (jsonReadKeyExpectZ(json, MANIFEST_KEY_CHECKSUM_PAGE_ERROR)) + file->checksumPageErrorList = jsonFromVar(jsonReadVar(json)); + } - // Bundle info - if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_BUNDLE_ID)) - { - file.bundleId = jsonReadUInt64(json); + // Group + if (jsonReadKeyExpectZ(json, MANIFEST_KEY_GROUP)) + file->group = manifestOwnerGet(jsonReadVar(json)); + else + file->group = manifest->fileGroupDefault; - if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_BUNDLE_OFFSET)) - file.bundleOffset = jsonReadUInt64(json); - } + // Mode + if (jsonReadKeyExpectZ(json, MANIFEST_KEY_MODE)) + file->mode = cvtZToMode(strZ(jsonReadStr(json))); + else + file->mode = manifest->fileModeDefault; - // The checksum might not exist if this is a partial save that was done during the backup to preserve checksums for already - // backed up files - if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_CHECKSUM)) - file.checksumSha1 = bufPtr(bufNewDecode(encodingHex, jsonReadStr(json))); + // The repo checksum might not exist if this is a partial save that was done during the backup to preserve checksums for + // already backed up files or if this is an older manifest + if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_CHECKSUM_REPO)) + file->checksumRepoSha1 = bufPtr(bufNewDecode(encodingHex, jsonReadStr(json))); - // Page checksum errors - if (jsonReadKeyExpectZ(json, MANIFEST_KEY_CHECKSUM_PAGE)) - { - file.checksumPage = true; - file.checksumPageError = !jsonReadBool(json); + // Reference + if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_REFERENCE)) + { + file->reference = jsonReadStr(json); - if (jsonReadKeyExpectZ(json, MANIFEST_KEY_CHECKSUM_PAGE_ERROR)) - file.checksumPageErrorList = jsonFromVar(jsonReadVar(json)); - } + if (!loadData->referenceListFound) + file->reference = strLstAddIfMissing(manifest->pub.referenceList, file->reference); + } - // Group - if (jsonReadKeyExpectZ(json, MANIFEST_KEY_GROUP)) - file.group = manifestOwnerGet(jsonReadVar(json)); - else - file.group = manifest->fileGroupDefault; + // If "repo-size" is not present in the manifest file, then it is the same as size (i.e. uncompressed) - to save space, + // the repo-size is only stored in the manifest file if it is different than size. + const bool sizeRepoExists = jsonReadKeyExpectStrId(json, MANIFEST_KEY_SIZE_REPO); - // Mode - if (jsonReadKeyExpectZ(json, MANIFEST_KEY_MODE)) - file.mode = cvtZToMode(strZ(jsonReadStr(json))); - else - file.mode = manifest->fileModeDefault; + if (sizeRepoExists) + file->sizeRepo = jsonReadUInt64(json); - // The repo checksum might not exist if this is a partial save that was done during the backup to preserve checksums for - // already backed up files or if this is an older manifest - if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_CHECKSUM_REPO)) - file.checksumRepoSha1 = bufPtr(bufNewDecode(encodingHex, jsonReadStr(json))); + // Size is required so error if it is not present. Older versions removed the size before the backup to ensure that the + // manifest was updated during the backup, so size can be missing in partial manifests. This error will prevent older + // partials from being resumed. + if (!jsonReadKeyExpectStrId(json, MANIFEST_KEY_SIZE)) + THROW_FMT(FormatError, "missing size for file '%s'", strZ(key)); - // Reference - if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_REFERENCE)) - { - file.reference = jsonReadStr(json); + file->size = jsonReadUInt64(json); - if (!loadData->referenceListFound) - file.reference = strLstAddIfMissing(manifest->pub.referenceList, file.reference); - } + // If repo size did not exist then + if (!sizeRepoExists) + file->sizeRepo = file->size; - // If "repo-size" is not present in the manifest file, then it is the same as size (i.e. uncompressed) - to save space, - // the repo-size is only stored in the manifest file if it is different than size. - const bool sizeRepoExists = jsonReadKeyExpectStrId(json, MANIFEST_KEY_SIZE_REPO); + // If file size is zero then assign the static zero hash + if (file->size == 0) + file->checksumSha1 = bufPtrConst(HASH_TYPE_SHA1_ZERO_BUF); - if (sizeRepoExists) - file.sizeRepo = jsonReadUInt64(json); + // If original is not present in the manifest file then it is the same as size (i.e. the file did not change size during + // copy) -- to save space the original size is only stored in the manifest file if it is different than size. + if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_SIZE_ORIGINAL)) + file->sizeOriginal = jsonReadUInt64(json); + else + file->sizeOriginal = file->size; - // Size is required so error if it is not present. Older versions removed the size before the backup to ensure that the - // manifest was updated during the backup, so size can be missing in partial manifests. This error will prevent older - // partials from being resumed. - if (!jsonReadKeyExpectStrId(json, MANIFEST_KEY_SIZE)) - THROW_FMT(FormatError, "missing size for file '%s'", strZ(key)); + // Timestamp is required so error if it is not present + if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_TIMESTAMP)) + file->timestamp = (time_t)jsonReadInt64(json); + else + THROW_FMT(FormatError, "missing timestamp for file '%s'", strZ(key)); - file.size = jsonReadUInt64(json); + // User + if (jsonReadKeyExpectZ(json, MANIFEST_KEY_USER)) + file->user = manifestOwnerGet(jsonReadVar(json)); + else + file->user = manifest->fileUserDefault; +} - // If repo size did not exist then - if (!sizeRepoExists) - file.sizeRepo = file.size; +static void +manifestLoadCallback(void *const callbackData, const String *const section, const String *const key, const String *const value) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM_P(VOID, callbackData); + FUNCTION_TEST_PARAM(STRING, section); + FUNCTION_TEST_PARAM(STRING, key); + FUNCTION_TEST_PARAM(STRING, value); + FUNCTION_TEST_END(); - // If file size is zero then assign the static zero hash - if (file.size == 0) - file.checksumSha1 = bufPtrConst(HASH_TYPE_SHA1_ZERO_BUF); + FUNCTION_AUDIT_CALLBACK(); - // If original is not present in the manifest file then it is the same as size (i.e. the file did not change size during - // copy) -- to save space the original size is only stored in the manifest file if it is different than size. - if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_SIZE_ORIGINAL)) - file.sizeOriginal = jsonReadUInt64(json); - else - file.sizeOriginal = file.size; + ASSERT(callbackData != NULL); + ASSERT(section != NULL); + ASSERT(key != NULL); - // Timestamp is required so error if it is not present - if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_TIMESTAMP)) - file.timestamp = (time_t)jsonReadInt64(json); - else - THROW_FMT(FormatError, "missing timestamp for file '%s'", strZ(key)); + ManifestLoadData *const loadData = (ManifestLoadData *)callbackData; + Manifest *const manifest = loadData->manifest; - // User - if (jsonReadKeyExpectZ(json, MANIFEST_KEY_USER)) - file.user = manifestOwnerGet(jsonReadVar(json)); - else - file.user = manifest->fileUserDefault; + // ----------------------------------------------------------------------------------------------------------------------------- + if (strEqZ(section, MANIFEST_SECTION_TARGET_FILE)) + { + ManifestFile file = {.name = key}; + + manifestLoadFileEntry(manifest, loadData, key, value, &file); manifestFileAdd(manifest, &file); } // ----------------------------------------------------------------------------------------------------------------------------- + else if (strEqZ(section, MANIFEST_SECTION_CUSTOM_FILE)) + { + ManifestFile file = {.name = key}; + + manifestLoadFileEntry(manifest, loadData, key, value, &file); + + manifestCustomFileAdd(manifest, &file); + } + // ----------------------------------------------------------------------------------------------------------------------------- else if (strEqZ(section, MANIFEST_SECTION_TARGET_PATH)) { ManifestPath path = {.name = key}; @@ -2502,6 +2541,85 @@ manifestOwnerVar(const String *const ownerDefault) FUNCTION_TEST_RETURN_CONST(VARIANT, ownerDefault == NULL ? BOOL_FALSE_VAR : varNewStr(ownerDefault)); } + +static void manifestSaveFileEntry(const ManifestFile *file, ManifestSaveData *const saveData, const char *section, InfoSave *const infoSaveData) +{ + JsonWrite *const json = jsonWriteObjectBegin(jsonWriteNewP()); + + // Block incremental info + if (file->blockIncrSize != 0) + { + jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_BLOCK_INCR), file->blockIncrSize / BLOCK_INCR_SIZE_FACTOR); + + if (file->blockIncrChecksumSize != BLOCK_INCR_CHECKSUM_SIZE_MIN) + jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_BLOCK_INCR_CHECKSUM), file->blockIncrChecksumSize); + + if (file->blockIncrMapSize != 0) + jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_BLOCK_INCR_MAP), file->blockIncrMapSize); + } + + // Bundle info + if (file->bundleId != 0) + { + jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_BUNDLE_ID), file->bundleId); + + if (file->bundleOffset != 0) + jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_BUNDLE_OFFSET), file->bundleOffset); + } + + // Save if the file size is not zero and the checksum exists. The checksum might not exist if this is a partial save + // performed during a backup. + if (file->size != 0 && file->checksumSha1 != NULL) + { + jsonWriteStr( + jsonWriteKeyStrId(json, MANIFEST_KEY_CHECKSUM), + strNewEncode(encodingHex, BUF(file->checksumSha1, HASH_TYPE_SHA1_SIZE))); + } + + if (file->checksumPage) + { + jsonWriteBool(jsonWriteKeyZ(json, MANIFEST_KEY_CHECKSUM_PAGE), !file->checksumPageError); + + if (file->checksumPageErrorList != NULL) + jsonWriteJson(jsonWriteKeyZ(json, MANIFEST_KEY_CHECKSUM_PAGE_ERROR), file->checksumPageErrorList); + } + + if (!varEq(manifestOwnerVar(file->group), saveData->groupDefault)) + jsonWriteVar(jsonWriteKeyZ(json, MANIFEST_KEY_GROUP), manifestOwnerVar(file->group)); + + if (file->mode != saveData->fileModeDefault) + jsonWriteStrFmt(jsonWriteKeyZ(json, MANIFEST_KEY_MODE), "%04o", file->mode); + + // Save if the repo checksum is not null. The repo checksum for zero-length files may vary depending on compression + // and encryption applied. + if (file->checksumRepoSha1 != NULL) + { + jsonWriteStr( + jsonWriteKeyStrId(json, MANIFEST_KEY_CHECKSUM_REPO), + strNewEncode(encodingHex, BUF(file->checksumRepoSha1, HASH_TYPE_SHA1_SIZE))); + } + + if (file->reference != NULL) + jsonWriteStr(jsonWriteKeyStrId(json, MANIFEST_KEY_REFERENCE), file->reference); + + if (file->sizeRepo != file->size) + jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_SIZE_REPO), file->sizeRepo); + + jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_SIZE), file->size); + + if (file->sizeOriginal != file->size) + jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_SIZE_ORIGINAL), file->sizeOriginal); + + jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_TIMESTAMP), (uint64_t)file->timestamp); + + if (!varEq(manifestOwnerVar(file->user), saveData->userDefault)) + jsonWriteVar(jsonWriteKeyZ(json, MANIFEST_KEY_USER), manifestOwnerVar(file->user)); + + infoSaveValue( + infoSaveData, section, strZ(file->name), jsonWriteResult(jsonWriteObjectEnd(json))); + +} + static void manifestSaveCallback(void *const callbackData, const String *const sectionNext, InfoSave *const infoSaveData) { @@ -2727,6 +2845,23 @@ manifestSaveCallback(void *const callbackData, const String *const sectionNext, MEM_CONTEXT_TEMP_END(); } + // ----------------------------------------------------------------------------------------------------------------------------- + if (infoSaveSection(infoSaveData, MANIFEST_SECTION_CUSTOM_FILE, sectionNext)) + { + MEM_CONTEXT_TEMP_RESET_BEGIN() + { + for (unsigned int fileIdx = 0; fileIdx < manifestCustomFileTotal(manifest); fileIdx++) + { + const ManifestFile file = manifestCustomFile(manifest, fileIdx); + + manifestSaveFileEntry(&file, saveData, MANIFEST_SECTION_CUSTOM_FILE, infoSaveData); + + MEM_CONTEXT_TEMP_RESET(1000); + } + } + MEM_CONTEXT_TEMP_END(); + } + // ----------------------------------------------------------------------------------------------------------------------------- if (infoSaveSection(infoSaveData, MANIFEST_SECTION_DB, sectionNext)) { @@ -2767,79 +2902,8 @@ manifestSaveCallback(void *const callbackData, const String *const sectionNext, for (unsigned int fileIdx = 0; fileIdx < manifestFileTotal(manifest); fileIdx++) { const ManifestFile file = manifestFile(manifest, fileIdx); - JsonWrite *const json = jsonWriteObjectBegin(jsonWriteNewP()); - - // Block incremental info - if (file.blockIncrSize != 0) - { - jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_BLOCK_INCR), file.blockIncrSize / BLOCK_INCR_SIZE_FACTOR); - - if (file.blockIncrChecksumSize != BLOCK_INCR_CHECKSUM_SIZE_MIN) - jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_BLOCK_INCR_CHECKSUM), file.blockIncrChecksumSize); - - if (file.blockIncrMapSize != 0) - jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_BLOCK_INCR_MAP), file.blockIncrMapSize); - } - - // Bundle info - if (file.bundleId != 0) - { - jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_BUNDLE_ID), file.bundleId); - - if (file.bundleOffset != 0) - jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_BUNDLE_OFFSET), file.bundleOffset); - } - - // Save if the file size is not zero and the checksum exists. The checksum might not exist if this is a partial save - // performed during a backup. - if (file.size != 0 && file.checksumSha1 != NULL) - { - jsonWriteStr( - jsonWriteKeyStrId(json, MANIFEST_KEY_CHECKSUM), - strNewEncode(encodingHex, BUF(file.checksumSha1, HASH_TYPE_SHA1_SIZE))); - } - - if (file.checksumPage) - { - jsonWriteBool(jsonWriteKeyZ(json, MANIFEST_KEY_CHECKSUM_PAGE), !file.checksumPageError); - - if (file.checksumPageErrorList != NULL) - jsonWriteJson(jsonWriteKeyZ(json, MANIFEST_KEY_CHECKSUM_PAGE_ERROR), file.checksumPageErrorList); - } - - if (!varEq(manifestOwnerVar(file.group), saveData->groupDefault)) - jsonWriteVar(jsonWriteKeyZ(json, MANIFEST_KEY_GROUP), manifestOwnerVar(file.group)); - - if (file.mode != saveData->fileModeDefault) - jsonWriteStrFmt(jsonWriteKeyZ(json, MANIFEST_KEY_MODE), "%04o", file.mode); - - // Save if the repo checksum is not null. The repo checksum for zero-length files may vary depending on compression - // and encryption applied. - if (file.checksumRepoSha1 != NULL) - { - jsonWriteStr( - jsonWriteKeyStrId(json, MANIFEST_KEY_CHECKSUM_REPO), - strNewEncode(encodingHex, BUF(file.checksumRepoSha1, HASH_TYPE_SHA1_SIZE))); - } - - if (file.reference != NULL) - jsonWriteStr(jsonWriteKeyStrId(json, MANIFEST_KEY_REFERENCE), file.reference); - - if (file.sizeRepo != file.size) - jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_SIZE_REPO), file.sizeRepo); - - jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_SIZE), file.size); - - if (file.sizeOriginal != file.size) - jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_SIZE_ORIGINAL), file.sizeOriginal); - - jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_TIMESTAMP), (uint64_t)file.timestamp); - - if (!varEq(manifestOwnerVar(file.user), saveData->userDefault)) - jsonWriteVar(jsonWriteKeyZ(json, MANIFEST_KEY_USER), manifestOwnerVar(file.user)); - - infoSaveValue( - infoSaveData, MANIFEST_SECTION_TARGET_FILE, strZ(file.name), jsonWriteResult(jsonWriteObjectEnd(json))); + + manifestSaveFileEntry(&file, saveData, MANIFEST_SECTION_TARGET_FILE, infoSaveData); MEM_CONTEXT_TEMP_RESET(1000); } diff --git a/src/info/manifest.h b/src/info/manifest.h index 0bf04148a2..ed3fd54ea3 100644 --- a/src/info/manifest.h +++ b/src/info/manifest.h @@ -236,6 +236,7 @@ typedef struct ManifestPub List *linkList; // List of links List *pathList; // List of paths List *targetList; // List of targets + List *customFileList; // List of custom files StringList *referenceList; // List of file references } ManifestPub; @@ -342,6 +343,13 @@ manifestFilePackGet(const Manifest *const this, const unsigned int fileIdx) return *(ManifestFilePack **)lstGet(THIS_PUB(Manifest)->fileList, fileIdx); } +// Get custom file in pack format by index +FN_INLINE_ALWAYS const ManifestFilePack * +manifestCustomFilePackGet(const Manifest *const this, const unsigned int fileIdx) +{ + return *(ManifestFilePack **)lstGet(THIS_PUB(Manifest)->customFileList, fileIdx); +} + // Get file name FN_INLINE_ALWAYS const String * manifestFileNameGet(const Manifest *const this, const unsigned int fileIdx) @@ -356,9 +364,19 @@ manifestFile(const Manifest *const this, const unsigned int fileIdx) return manifestFileUnpack(this, manifestFilePackGet(this, fileIdx)); } +// Get custom file by index +FN_INLINE_ALWAYS ManifestFile +manifestCustomFile(const Manifest *const this, const unsigned int fileIdx) +{ + return manifestFileUnpack(this, manifestCustomFilePackGet(this, fileIdx)); +} + // Add a file FN_EXTERN void manifestFileAdd(Manifest *this, ManifestFile *file); +// Add a file to the custom file list +FN_EXTERN void manifestCustomFileAdd(Manifest *this, ManifestFile *file); + // Find file in pack format by name FN_EXTERN const ManifestFilePack *manifestFilePackFind(const Manifest *this, const String *name); @@ -386,6 +404,12 @@ manifestFileTotal(const Manifest *const this) return lstSize(THIS_PUB(Manifest)->fileList); } +FN_INLINE_ALWAYS unsigned int +manifestCustomFileTotal(const Manifest *const this) +{ + return lstSize(THIS_PUB(Manifest)->customFileList); +} + // Update a file with new data FN_EXTERN void manifestFileUpdate(Manifest *const this, const ManifestFile *file); From ceab35805226c6c7d42a6de947ee4e7d991c62f1 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Tue, 3 Feb 2026 17:46:25 +0300 Subject: [PATCH 04/21] implement test --- src/build/config/config.yaml | 1 + src/command/repo/push.c | 41 ++++--- src/common/type/string.c | 21 ++++ src/common/type/string.h | 3 + src/config/parse.auto.c.inc | 3 + test/define.yaml | 3 +- test/src/module/command/repoTest.c | 173 +++++++++++++++++++++++++++++ 7 files changed, 226 insertions(+), 19 deletions(-) diff --git a/src/build/config/config.yaml b/src/build/config/config.yaml index 15d84757c3..aa8a77d250 100644 --- a/src/build/config/config.yaml +++ b/src/build/config/config.yaml @@ -831,6 +831,7 @@ option: manifest: {} repo-get: {} repo-ls: {} + repo-push: {} repo-put: {} restore: {} stanza-create: {} diff --git a/src/command/repo/push.c b/src/command/repo/push.c index f494f51507..dae657a2fd 100644 --- a/src/command/repo/push.c +++ b/src/command/repo/push.c @@ -14,18 +14,22 @@ Repository Put Command #include "common/log.h" #include "common/compress/helper.h" #include "common/memContext.h" +#include "common/type/string.h" #include "config/config.h" #include "storage/helper.h" #include "info/manifest.h" + static String * -composeDestinationPath(const String *source, const String *stanza, const String *backupLabel) +composeDestinationPath(const String *stanza, const String *backupLabel, const String *fileName) { FUNCTION_LOG_BEGIN(logLevelDebug); - FUNCTION_LOG_PARAM(STRING, source); + FUNCTION_LOG_PARAM(STRING, stanza); + FUNCTION_LOG_PARAM(STRING, backupLabel); + FUNCTION_LOG_PARAM(STRING, fileName); FUNCTION_LOG_END(); - String *const result = strNewFmt("%s/%s/%s", strZ(stanza), strZ(backupLabel), strZ(source)); + String *const result = strNewFmt("%s/%s/%s", strZ(stanza), strZ(backupLabel), strZ(fileName)); FUNCTION_LOG_RETURN(STRING, result); } @@ -44,28 +48,29 @@ storagePushProcess(const String *file, CompressType compressType, int compressLe FUNCTION_LOG_END(); // Ensure that the file exists and readable + MEM_CONTEXT_TEMP_BEGIN() + { - // Normalize source file path - // Get current working dir - char currentWorkDir[1024]; - THROW_ON_SYS_ERROR(getcwd(currentWorkDir, sizeof(currentWorkDir)) == NULL, FormatError, "unable to get cwd"); + // Normalize source file path + // Get current working dir + char currentWorkDir[1024]; + THROW_ON_SYS_ERROR(getcwd(currentWorkDir, sizeof(currentWorkDir)) == NULL, FormatError, "unable to get cwd"); - // TODO: Use realpath() to normalize on posix. + // TODO: Use realpath() to normalize on posix. - String *sourcePath = strPathAbsolute(file, strNewZ(currentWorkDir)); + String *sourcePath = strPathAbsolute(file, strNewZ(currentWorkDir)); - // Repository Path Formation + // Repository Path Formation - const String *stanza = cfgOptionStr(cfgOptStanza); - const String *backupLabel = cfgOptionStr(cfgOptSet); + const String *stanza = cfgOptionStr(cfgOptStanza); + const String *backupLabel = cfgOptionStr(cfgOptSet); - String *destPath = composeDestinationPath(file, stanza, backupLabel); + String *destFilename = strFileName(file); + String *destPath = composeDestinationPath(stanza, backupLabel, destFilename); - // Is path valid for repo? - destPath = repoPathIsValid(destPath); + // Is path valid for repo? + destPath = repoPathIsValid(destPath); - MEM_CONTEXT_TEMP_BEGIN() - { bool repoChecksum = false; const Storage *storage = storageRepoWrite(); const StorageWrite *const destination = storageNewWriteP(storage, destPath); @@ -140,7 +145,7 @@ storagePushProcess(const String *file, CompressType compressType, int compressLe uint64_t size = pckReadU64P(ioFilterGroupResultP(filterGroup, SIZE_FILTER_TYPE)); ManifestFile customFile = { - .name = destPath, + .name = destFilename, .mode = basePath->mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), .user = basePath->user, .group = basePath->group, diff --git a/src/common/type/string.c b/src/common/type/string.c index da7ea27d8d..8a0b82588a 100644 --- a/src/common/type/string.c +++ b/src/common/type/string.c @@ -796,6 +796,27 @@ strPath(const String *const this) end - this->pub.buffer <= 1 ? (size_t)(end - this->pub.buffer) : (size_t)(end - this->pub.buffer - 1))); } +/**********************************************************************************************************************************/ +FN_EXTERN String * +strFileName(const String *const this) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(STRING, this); + FUNCTION_TEST_END(); + + ASSERT(this != NULL); + + const char *end = this->pub.buffer + strSize(this); + const char *ptr = end; + + while (ptr > this->pub.buffer && *(ptr - 1) != '/') + ptr--; + + FUNCTION_TEST_RETURN( + STRING, + strNewZN(ptr, (size_t)(end - ptr))); +} + /**********************************************************************************************************************************/ FN_EXTERN String * strPathAbsolute(const String *const this, const String *const base) diff --git a/src/common/type/string.h b/src/common/type/string.h index 50f89ae892..5413890e57 100644 --- a/src/common/type/string.h +++ b/src/common/type/string.h @@ -197,6 +197,9 @@ FN_EXTERN String *strLower(String *this); // Return the path part of a string (i.e. everything before the last / or "" if there is no /) FN_EXTERN String *strPath(const String *this); +// Return the file name part of a string (i.e. everything after the last / or "" if there is no /) +FN_EXTERN String *strFileName(const String *const this); + // Combine with a base path to get an absolute path FN_EXTERN String *strPathAbsolute(const String *this, const String *base); diff --git a/src/config/parse.auto.c.inc b/src/config/parse.auto.c.inc index 3ea358c2a3..3b8150d349 100644 --- a/src/config/parse.auto.c.inc +++ b/src/config/parse.auto.c.inc @@ -1844,6 +1844,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(Manifest) // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/compress-level-network + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(Restore) // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(StanzaCreate) // opt/compress-level-network @@ -1856,6 +1857,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = ( // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/compress-level-network + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/compress-level-network ), // opt/compress-level-network // opt/compress-level-network PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/compress-level-network @@ -1863,6 +1865,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] = PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(Backup) // opt/compress-level-network + PARSE_RULE_OPTION_COMMAND(RepoPush) // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(Restore) // opt/compress-level-network PARSE_RULE_OPTION_COMMAND(Verify) // opt/compress-level-network ), // opt/compress-level-network diff --git a/test/define.yaml b/test/define.yaml index d2fe3861ad..70da67be02 100644 --- a/test/define.yaml +++ b/test/define.yaml @@ -1023,12 +1023,13 @@ unit: # ---------------------------------------------------------------------------------------------------------------------------- - name: repo - total: 3 + total: 4 coverage: - command/repo/common - command/repo/get - command/repo/ls + - command/repo/push - command/repo/put - command/repo/rm diff --git a/test/src/module/command/repoTest.c b/test/src/module/command/repoTest.c index 44d3cbe295..f4b1bc2f2c 100644 --- a/test/src/module/command/repoTest.c +++ b/test/src/module/command/repoTest.c @@ -3,15 +3,65 @@ Test Repo Commands ***********************************************************************************************************************************/ #include "common/io/bufferRead.h" #include "common/io/bufferWrite.h" +#include "common/memContext.h" +#include "common/type/string.h" #include "storage/posix/storage.h" +#include "storage/helper.h" #include "common/harnessConfig.h" #include "common/harnessInfo.h" #include "common/harnessStorageHelper.h" +#include "common/harnessPostgres.h" #include "info/infoArchive.h" #include "info/infoBackup.h" +static String * +testManifestCustomFilesValidate(const Storage *const storage, const String *const path) +{ + FUNCTION_HARNESS_BEGIN(); + FUNCTION_HARNESS_PARAM(STORAGE, storage); + FUNCTION_HARNESS_PARAM(STRING, path); + FUNCTION_HARNESS_END(); + + ASSERT(storage != NULL); + ASSERT(path != NULL); + + String *const result = strNew(); + + MEM_CONTEXT_TEMP_BEGIN() + { + Manifest *manifest = manifestLoadFile( + storageRepo(), strNewFmt("%s/" BACKUP_MANIFEST_FILE, strZ(path)), cipherTypeNone, NULL); + + + // Build list of files in the manifest + StringList *const manifestFileList = strLstNew(); + + for (unsigned int fileIdx = 0; fileIdx < manifestCustomFileTotal(manifest); fileIdx++) + strLstAdd(manifestFileList, manifestFileUnpack(manifest, manifestCustomFilePackGet(manifest, fileIdx)).name); + + for (unsigned int manifestFileIdx = 0; manifestFileIdx < strLstSize(manifestFileList); manifestFileIdx++) + { + // const ManifestFilePack *const filePack = manifestFilePackFind( + // manifest, strLstGet(manifestFileList, manifestFileIdx)); + + // const ManifestFile file = manifestFileUnpack(manifest, filePack); + + // Error if reference is NULL + // if (file.reference == NULL) + // THROW_FMT(AssertError, "manifest file '%s' not in backup but does not have a reference", strZ(file.name)); + + strCatFmt(result, "%s\n", strZ(strLstGet(manifestFileList, manifestFileIdx))); + // strCatFmt(result, "%s\n", strZ(file.name)); + } + + } + MEM_CONTEXT_TEMP_END(); + + FUNCTION_HARNESS_RETURN(STRING, result); +} + /*********************************************************************************************************************************** Test Run ***********************************************************************************************************************************/ @@ -881,5 +931,128 @@ testRun(void) TEST_STORAGE_LIST(storageRepo(), NULL, "path/\n", .comment = "check path exists and file removed"); } + #define TEST_BACKUP_LABEL_FULL "20260201-173010F" + #define TEST_STANZA "testStanza01" + + #define TEST_MANIFEST_HEADER \ + "[backup]\n" \ + "backup-label=null\n" \ + "backup-timestamp-copy-start=0\n" \ + "backup-timestamp-start=0\n" \ + "backup-timestamp-stop=0\n" \ + "backup-type=\"full\"\n" + + #define TEST_MANIFEST_OPTION_ALL \ + "\n" \ + "[backup:option]\n" \ + "option-archive-check=false\n" \ + "option-archive-copy=false\n" \ + "option-checksum-page=false\n" \ + "option-compress=false\n" \ + "option-compress-type=\"none\"\n" \ + "option-hardlink=false\n" \ + "option-online=false\n" + + #define TEST_MANIFEST_TARGET \ + "\n" \ + "[backup:target]\n" \ + "pg_data={\"path\":\"/pg/base\",\"type\":\"path\"}\n" + + #define TEST_MANIFEST_DB \ + "\n" \ + "[db]\n" \ + "postgres={\"db-id\":12173,\"db-last-system-id\":12168}\n" + #define TEST_MANIFEST_FILE \ + "\n" \ + "[target:file]\n" \ + "pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"size\":4,\"timestamp\":1565282114}\n" + + #define TEST_MANIFEST_FILE_DEFAULT \ + "\n" \ + "[target:file:default]\n" \ + "group=\"group1\"\n" \ + "mode=\"0600\"\n" \ + "user=\"user1\"\n" + + #define TEST_MANIFEST_LINK \ + "\n" \ + "[target:link]\n" \ + "pg_data/pg_stat={\"destination\":\"../pg_stat\"}\n" + + #define TEST_MANIFEST_LINK_DEFAULT \ + "\n" \ + "[target:link:default]\n" \ + "group=\"group1\"\n" \ + "user=false\n" + + #define TEST_MANIFEST_PATH \ + "\n" \ + "[target:path]\n" \ + "pg_data={\"user\":\"user1\"}\n" \ + + #define TEST_MANIFEST_PATH_DEFAULT \ + "\n" \ + "[target:path:default]\n" \ + "group=false\n" \ + "mode=\"0700\"\n" \ + "user=\"user1\"\n" + + + // ***************************************************************************************************************************** + if (testBegin("cmdStoragePush()")) + { + HRN_STORAGE_PATH_CREATE(storageRepoWrite(), "path"); + + Storage *storageTest = storagePosixNewP(TEST_PATH_STR, .write = true); + + HRN_STORAGE_PUT_Z(storageTest, "path/aaa.txt", "TESTDATA", .timeModified = 1578671569); + + TEST_TITLE("push uncompressed file"); + + StringList *argList = strLstNew(); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, TEST_PATH "/bogus"); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, TEST_PATH "/repo"); + hrnCfgArgRawZ(argList, cfgOptStanza, TEST_STANZA); + hrnCfgArgRawZ(argList, cfgOptSet, TEST_BACKUP_LABEL_FULL); + hrnCfgArgRawZ(argList, cfgOptRepo, "2"); + strLstAddZ(argList, "path/aaa.txt"); + + HRN_INFO_PUT( + storageRepoWrite(), STORAGE_REPO_BACKUP "/" TEST_STANZA "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE, + TEST_MANIFEST_HEADER + "\n" + "[backup:db]\n" + "db-catalog-version=201608131\n" + "db-control-version=960\n" + "db-id=1\n" + "db-system-id=" HRN_PG_SYSTEMID_94_Z "\n" // 9.4 system id is used so version will trigger error + "db-version=\"9.6\"\n" + TEST_MANIFEST_OPTION_ALL + TEST_MANIFEST_TARGET + TEST_MANIFEST_DB + TEST_MANIFEST_FILE + TEST_MANIFEST_FILE_DEFAULT + TEST_MANIFEST_LINK + TEST_MANIFEST_LINK_DEFAULT + TEST_MANIFEST_PATH + TEST_MANIFEST_PATH_DEFAULT, + .comment = "manifest db section mismatch"); + + HRN_CFG_LOAD(cfgCmdRepoPush, argList); + + TEST_RESULT_VOID(cmdStoragePush(), "push file"); + TEST_RESULT_LOG("P00 INFO: push file path/aaa.txt to the archive."); + TEST_STORAGE_LIST(storageRepo(), "testStanza01/20260201-173010F", "aaa.txt\n", .comment = "check path exists and file added"); + + TEST_STORAGE_GET(storageRepo(), "testStanza01/20260201-173010F/aaa.txt", "TESTDATA"); + + /* Check manifest record */ + TEST_RESULT_STR_Z( + testManifestCustomFilesValidate(storageRepo(), STRDEF(STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL)), + "aaa.txt\n", + "compare file list"); + TEST_TITLE("push compressed file"); + } + FUNCTION_HARNESS_RETURN_VOID(); } From e42ad9a55195963f4074a741b26bdfbfe8545582 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Wed, 4 Feb 2026 23:05:30 +0300 Subject: [PATCH 05/21] remove encryption and make compression test, format code --- src/command/repo/push.c | 130 ++++++++++++------------ src/info/manifest.c | 66 ++++++++++++ src/info/manifest.h | 3 + test/src/module/command/repoTest.c | 155 ++++++++++++++++++++++------- 4 files changed, 252 insertions(+), 102 deletions(-) diff --git a/src/command/repo/push.c b/src/command/repo/push.c index dae657a2fd..5f1e16e5fe 100644 --- a/src/command/repo/push.c +++ b/src/command/repo/push.c @@ -6,19 +6,17 @@ Repository Put Command #include #include "command/repo/common.h" -#include "common/crypto/cipherBlock.h" +#include "common/compress/helper.h" #include "common/debug.h" #include "common/io/fdRead.h" #include "common/io/filter/size.h" #include "common/io/io.h" #include "common/log.h" -#include "common/compress/helper.h" #include "common/memContext.h" #include "common/type/string.h" #include "config/config.h" -#include "storage/helper.h" #include "info/manifest.h" - +#include "storage/helper.h" static String * composeDestinationPath(const String *stanza, const String *backupLabel, const String *fileName) @@ -34,7 +32,6 @@ composeDestinationPath(const String *stanza, const String *backupLabel, const St FUNCTION_LOG_RETURN(STRING, result); } - /*********************************************************************************************************************************** Write source IO to destination file ***********************************************************************************************************************************/ @@ -50,22 +47,21 @@ storagePushProcess(const String *file, CompressType compressType, int compressLe // Ensure that the file exists and readable MEM_CONTEXT_TEMP_BEGIN() { - // Normalize source file path // Get current working dir char currentWorkDir[1024]; THROW_ON_SYS_ERROR(getcwd(currentWorkDir, sizeof(currentWorkDir)) == NULL, FormatError, "unable to get cwd"); - // TODO: Use realpath() to normalize on posix. - String *sourcePath = strPathAbsolute(file, strNewZ(currentWorkDir)); // Repository Path Formation - const String *stanza = cfgOptionStr(cfgOptStanza); + const String *stanza = cfgOptionStr(cfgOptStanza); const String *backupLabel = cfgOptionStr(cfgOptSet); - String *destFilename = strFileName(file); + String *destFilename = strCat(strNew(), strFileName(file)); + compressExtCat(destFilename, compressType); + String *destPath = composeDestinationPath(stanza, backupLabel, destFilename); // Is path valid for repo? @@ -75,102 +71,104 @@ storagePushProcess(const String *file, CompressType compressType, int compressLe const Storage *storage = storageRepoWrite(); const StorageWrite *const destination = storageNewWriteP(storage, destPath); - IoFilterGroup *const filterGroup = ioWriteFilterGroup(storageWriteIo(destination)); + IoRead *const sourceIo = storageReadIo(storageNewReadP(storageLocal(), sourcePath)); + IoWrite *const destinationIo = storageWriteIo(destination); - CipherType cipherType = cfgOptionStrId(cfgOptRepoCipherType); - const String *cipherPass = cfgOptionStrNull(cfgOptRepoCipherPass); + IoFilterGroup *const readFilterGroup = ioReadFilterGroup(sourceIo); + IoFilterGroup *const writeFilterGroup = ioWriteFilterGroup(destinationIo); const String *manifestFileName = strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strZ(backupLabel)); Manifest *manifest = manifestLoadFile( - storage, manifestFileName, - cipherType, cipherPass); + storage, manifestFileName, + cipherTypeNone, NULL); // Add SHA1 filter - ioFilterGroupAdd(filterGroup, cryptoHashNew(hashTypeSha1)); + ioFilterGroupAdd(writeFilterGroup, cryptoHashNew(hashTypeSha1)); // Add compression if (compressType != compressTypeNone) { ioFilterGroupAdd( - ioWriteFilterGroup(storageWriteIo(destination)), + writeFilterGroup, compressFilterP(compressType, cfgOptionInt(cfgOptCompressLevel))); repoChecksum = true; } - // Add encryption filter if required - if (manifestCipherSubPass(manifest) != NULL) - { - const CipherType repoCipherType = cfgOptionStrId(cfgOptRepoCipherType); - - if (repoCipherType != cipherTypeNone) - { - // Check for a passphrase parameter - const String *cipherPass = cfgOptionStrNull(cfgOptCipherPass); - - // If not passed as a parameter use the repo passphrase - if (cipherPass == NULL) - cipherPass = cfgOptionStr(cfgOptRepoCipherPass); - - ioFilterGroupAdd( - ioWriteFilterGroup(storageWriteIo(destination)), - cipherBlockNewP( - cipherModeEncrypt, repoCipherType, BUFSTR(manifestCipherSubPass(manifest)) - ) - ); - repoChecksum = true; - } - } + // Capture checksum of file stored in the repo if filters that modify the output have been applied + if (repoChecksum) + ioFilterGroupAdd(writeFilterGroup, cryptoHashNew(hashTypeSha1)); // Add size filter last to calculate repo size - ioFilterGroupAdd(filterGroup, ioSizeNew()); + ioFilterGroupAdd(writeFilterGroup, ioSizeNew()); - IoRead *const source = storageReadIo(storageNewReadP(storageLocal(), sourcePath)); + // Add size filter last to calculate source file size + ioFilterGroupAdd(readFilterGroup, ioSizeNew()); // Open source and destination - ioReadOpen(source); - ioWriteOpen(storageWriteIo(destination)); + ioReadOpen(sourceIo); + ioWriteOpen(destinationIo); // Copy data from source to destination - ioCopyP(source, storageWriteIo(destination)); + ioCopyP(sourceIo, destinationIo); // Close the source and destination - ioReadClose(source); - ioWriteClose(storageWriteIo(destination)); + ioReadClose(sourceIo); + ioWriteClose(destinationIo); // Use base path to set ownership and mode const ManifestPath *const basePath = manifestPathFind(manifest, MANIFEST_TARGET_PGDATA_STR); // Add to manifest - uint64_t size = pckReadU64P(ioFilterGroupResultP(filterGroup, SIZE_FILTER_TYPE)); + uint64_t rsize = pckReadU64P(ioFilterGroupResultP(readFilterGroup, SIZE_FILTER_TYPE)); + uint64_t wsize = pckReadU64P(ioFilterGroupResultP(writeFilterGroup, SIZE_FILTER_TYPE)); ManifestFile customFile = { .name = destFilename, .mode = basePath->mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), - .user = basePath->user, - .group = basePath->group, - .size = size, - .sizeOriginal = size, - .sizeRepo = size, + .size = wsize, + .sizeOriginal = rsize, + .sizeRepo = wsize, .timestamp = time(NULL), - .checksumSha1 = bufPtr(pckReadBinP(ioFilterGroupResultP(filterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 0))), + .checksumSha1 = bufPtr(pckReadBinP(ioFilterGroupResultP(writeFilterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 0))), }; if (repoChecksum) { - PackRead * packRead = ioFilterGroupResultP(filterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 1); + PackRead *packRead = ioFilterGroupResultP(writeFilterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 1); ASSERT(packRead != NULL); customFile.checksumRepoSha1 = bufPtr(pckReadBinP(packRead)); } - manifestCustomFileAdd(manifest, &customFile); + bool found = false; + + for (unsigned int fileIdx = 0; fileIdx < manifestCustomFileTotal(manifest); fileIdx++) + { + ManifestFile manifestFile = manifestFileUnpack(manifest, manifestCustomFilePackGet(manifest, fileIdx)); + + if (strCmp(manifestFile.name, destFilename) == 0){ + manifestFile.mode = customFile.mode; + manifestFile.size = customFile.size; + manifestFile.sizeOriginal = customFile.sizeOriginal; + manifestFile.sizeRepo = customFile.sizeRepo; + manifestFile.timestamp = customFile.timestamp; + manifestFile.checksumSha1 = customFile.checksumSha1; + + manifestCustomFileUpdate(manifest, &manifestFile); + found = true; + break; + } + } + + if (!found) + manifestCustomFileAdd(manifest, &customFile); // Save manifest IoWrite *const manifestWrite = storageWriteIo( - storageNewWriteP( - storageRepoWrite(), - manifestFileName - )); + storageNewWriteP( + storageRepoWrite(), + manifestFileName + )); manifestSave(manifest, manifestWrite); } @@ -196,15 +194,11 @@ cmdStoragePush(void) LOG_INFO_FMT( "push file %s to the archive.", - strZ(filename)); + strZ(filename)); - CompressType compressType = compressTypeNone; - if (cfgOptionValid(cfgOptCompress)) - { - compressType = compressTypeEnum(cfgOptionStrId(cfgOptCompressType)); - } - storagePushProcess(filename, compressType, - cfgOptionInt(cfgOptCompressLevel)); + storagePushProcess(filename, + compressTypeEnum(cfgOptionStrId(cfgOptCompressType)), + cfgOptionInt(cfgOptCompressLevel)); } MEM_CONTEXT_TEMP_END(); diff --git a/src/info/manifest.c b/src/info/manifest.c index fb0bd1825b..bd87af39a7 100644 --- a/src/info/manifest.c +++ b/src/info/manifest.c @@ -468,6 +468,31 @@ manifestFilePackUpdate(Manifest *const this, ManifestFilePack **const filePack, FUNCTION_TEST_RETURN_VOID(); } +// Update custom file pack by creating a new one and then freeing the old one +static void +manifestCustomFilePackUpdate(Manifest *const this, ManifestFilePack **const filePack, const ManifestFile *const file) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(MANIFEST, this); + FUNCTION_TEST_PARAM_P(VOID, filePack); + FUNCTION_TEST_PARAM(MANIFEST_FILE, file); + FUNCTION_TEST_END(); + + ASSERT(this != NULL); + ASSERT(filePack != NULL); + ASSERT(file != NULL); + + MEM_CONTEXT_BEGIN(lstMemContext(this->pub.customFileList)) + { + ManifestFilePack *const filePackOld = *filePack; + *filePack = manifestFilePack(this, file); + memFree(filePackOld); + } + MEM_CONTEXT_END(); + + FUNCTION_TEST_RETURN_VOID(); +} + FN_EXTERN void manifestLinkAdd(Manifest *const this, const ManifestLink *const link) { @@ -3109,6 +3134,25 @@ manifestFilePackFindInternal(const Manifest *const this, const String *const nam FUNCTION_TEST_RETURN_TYPE_PP(ManifestFilePack, filePack); } +static ManifestFilePack ** +manifestCustomFilePackFindInternal(const Manifest *const this, const String *const name) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(MANIFEST, this); + FUNCTION_TEST_PARAM(STRING, name); + FUNCTION_TEST_END(); + + ASSERT(this != NULL); + ASSERT(name != NULL); + + ManifestFilePack **const filePack = lstFind(this->pub.customFileList, &name); + + if (filePack == NULL) + THROW_FMT(AssertError, "unable to find '%s' in manifest file list", strZ(name)); + + FUNCTION_TEST_RETURN_TYPE_PP(ManifestFilePack, filePack); +} + const ManifestFilePack * manifestFilePackFind(const Manifest *const this, const String *const name) { @@ -3162,6 +3206,28 @@ manifestFileUpdate(Manifest *const this, const ManifestFile *const file) FUNCTION_TEST_RETURN_VOID(); } +FN_EXTERN void +manifestCustomFileUpdate(Manifest *const this, const ManifestFile *file) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(MANIFEST, this); + FUNCTION_TEST_PARAM(MANIFEST_FILE, file); + FUNCTION_TEST_END(); + + ASSERT(this != NULL); + ASSERT(file != NULL); + ASSERT( + (!file->checksumPage && !file->checksumPageError && file->checksumPageErrorList == NULL) || + (file->checksumPage && !file->checksumPageError && file->checksumPageErrorList == NULL) || + (file->checksumPage && file->checksumPageError)); + ASSERT(file->size != 0 || (file->bundleId == 0 && file->bundleOffset == 0)); + + ManifestFilePack **const filePack = manifestCustomFilePackFindInternal(this, file->name); + manifestCustomFilePackUpdate(this, filePack, file); + + FUNCTION_TEST_RETURN_VOID(); +} + /*********************************************************************************************************************************** Link functions and getters/setters ***********************************************************************************************************************************/ diff --git a/src/info/manifest.h b/src/info/manifest.h index ed3fd54ea3..56e99aa008 100644 --- a/src/info/manifest.h +++ b/src/info/manifest.h @@ -413,6 +413,9 @@ manifestCustomFileTotal(const Manifest *const this) // Update a file with new data FN_EXTERN void manifestFileUpdate(Manifest *const this, const ManifestFile *file); +// Update a custom file with new data +FN_EXTERN void manifestCustomFileUpdate(Manifest *const this, const ManifestFile *file); + /*********************************************************************************************************************************** Link functions and getters/setters ***********************************************************************************************************************************/ diff --git a/test/src/module/command/repoTest.c b/test/src/module/command/repoTest.c index f4b1bc2f2c..4dd6ff1bc0 100644 --- a/test/src/module/command/repoTest.c +++ b/test/src/module/command/repoTest.c @@ -1,61 +1,41 @@ /*********************************************************************************************************************************** Test Repo Commands ***********************************************************************************************************************************/ +#include "common/compress/helper.h" #include "common/io/bufferRead.h" #include "common/io/bufferWrite.h" #include "common/memContext.h" #include "common/type/string.h" -#include "storage/posix/storage.h" #include "storage/helper.h" +#include "storage/posix/storage.h" #include "common/harnessConfig.h" #include "common/harnessInfo.h" -#include "common/harnessStorageHelper.h" #include "common/harnessPostgres.h" +#include "common/harnessStorageHelper.h" #include "info/infoArchive.h" #include "info/infoBackup.h" static String * -testManifestCustomFilesValidate(const Storage *const storage, const String *const path) +testManifestCustomFilesValidate(Manifest *manifest) { FUNCTION_HARNESS_BEGIN(); - FUNCTION_HARNESS_PARAM(STORAGE, storage); - FUNCTION_HARNESS_PARAM(STRING, path); FUNCTION_HARNESS_END(); - ASSERT(storage != NULL); - ASSERT(path != NULL); - String *const result = strNew(); MEM_CONTEXT_TEMP_BEGIN() { - Manifest *manifest = manifestLoadFile( - storageRepo(), strNewFmt("%s/" BACKUP_MANIFEST_FILE, strZ(path)), cipherTypeNone, NULL); - - // Build list of files in the manifest - StringList *const manifestFileList = strLstNew(); for (unsigned int fileIdx = 0; fileIdx < manifestCustomFileTotal(manifest); fileIdx++) - strLstAdd(manifestFileList, manifestFileUnpack(manifest, manifestCustomFilePackGet(manifest, fileIdx)).name); - - for (unsigned int manifestFileIdx = 0; manifestFileIdx < strLstSize(manifestFileList); manifestFileIdx++) { - // const ManifestFilePack *const filePack = manifestFilePackFind( - // manifest, strLstGet(manifestFileList, manifestFileIdx)); - - // const ManifestFile file = manifestFileUnpack(manifest, filePack); + const ManifestFile file = manifestFileUnpack(manifest, manifestCustomFilePackGet(manifest, fileIdx)); - // Error if reference is NULL - // if (file.reference == NULL) - // THROW_FMT(AssertError, "manifest file '%s' not in backup but does not have a reference", strZ(file.name)); - - strCatFmt(result, "%s\n", strZ(strLstGet(manifestFileList, manifestFileIdx))); - // strCatFmt(result, "%s\n", strZ(file.name)); + // strCatFmt(result, "%s\n", strZ(strLstGet(manifestFileList, manifestFileIdx))); + strCatFmt(result, "%s %lu %lu\n", strZ(file.name), file.size, file.sizeOriginal); } - } MEM_CONTEXT_TEMP_END(); @@ -934,6 +914,15 @@ testRun(void) #define TEST_BACKUP_LABEL_FULL "20260201-173010F" #define TEST_STANZA "testStanza01" + #define TEST_DATA \ + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, " \ + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " \ + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris " \ + "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in " \ + "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla " \ + "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " \ + "culpa qui officia deserunt mollit anim id est laborum." + #define TEST_MANIFEST_HEADER \ "[backup]\n" \ "backup-label=null\n" \ @@ -997,7 +986,6 @@ testRun(void) "mode=\"0700\"\n" \ "user=\"user1\"\n" - // ***************************************************************************************************************************** if (testBegin("cmdStoragePush()")) { @@ -1007,14 +995,97 @@ testRun(void) HRN_STORAGE_PUT_Z(storageTest, "path/aaa.txt", "TESTDATA", .timeModified = 1578671569); - TEST_TITLE("push uncompressed file"); + HRN_INFO_PUT( + storageRepoWrite(), STORAGE_REPO_BACKUP "/" TEST_STANZA "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE, + TEST_MANIFEST_HEADER + "\n" + "[backup:db]\n" + "db-catalog-version=201608131\n" + "db-control-version=960\n" + "db-id=1\n" + "db-system-id=" HRN_PG_SYSTEMID_94_Z "\n" // 9.4 system id is used so version will trigger error + "db-version=\"9.6\"\n" + TEST_MANIFEST_OPTION_ALL + TEST_MANIFEST_TARGET + TEST_MANIFEST_DB + TEST_MANIFEST_FILE + TEST_MANIFEST_FILE_DEFAULT + TEST_MANIFEST_LINK + TEST_MANIFEST_LINK_DEFAULT + TEST_MANIFEST_PATH + TEST_MANIFEST_PATH_DEFAULT, + .comment = "manifest db section mismatch"); + TEST_TITLE("missing file argument"); StringList *argList = strLstNew(); hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, TEST_PATH "/bogus"); hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, TEST_PATH "/repo"); hrnCfgArgRawZ(argList, cfgOptStanza, TEST_STANZA); hrnCfgArgRawZ(argList, cfgOptSet, TEST_BACKUP_LABEL_FULL); hrnCfgArgRawZ(argList, cfgOptRepo, "2"); + + HRN_CFG_LOAD(cfgCmdRepoPush, argList); + + TEST_ERROR( + cmdStoragePush(), ParamInvalidError, + "file parameter is required"); + + TEST_TITLE("push uncompressed file"); + + argList = strLstNew(); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, TEST_PATH "/bogus"); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, TEST_PATH "/repo"); + hrnCfgArgRawZ(argList, cfgOptCompressType, "none"); + hrnCfgArgRawZ(argList, cfgOptStanza, TEST_STANZA); + hrnCfgArgRawZ(argList, cfgOptSet, TEST_BACKUP_LABEL_FULL); + hrnCfgArgRawZ(argList, cfgOptRepo, "2"); + strLstAddZ(argList, "path/aaa.txt"); + + HRN_CFG_LOAD(cfgCmdRepoPush, argList); + + TEST_RESULT_VOID(cmdStoragePush(), "push file"); + TEST_RESULT_LOG("P00 INFO: push file path/aaa.txt to the archive."); + TEST_STORAGE_LIST(storageRepo(), "testStanza01/20260201-173010F", "aaa.txt\n", .comment = "check path exists and file added"); + + TEST_STORAGE_GET(storageRepo(), "testStanza01/20260201-173010F/aaa.txt", "TESTDATA"); + + Manifest *manifest = manifestLoadFile( + storageRepo(), STR(STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE), cipherTypeNone, NULL); + + /* Check manifest record */ + TEST_RESULT_STR_Z( + testManifestCustomFilesValidate(manifest), + "aaa.txt 8 8\n", + "compare file list"); + + TEST_TITLE("push uncompressed file, replace existing"); + + HRN_STORAGE_PUT_Z(storageTest, "path/aaa.txt", TEST_DATA, .timeModified = 1578671569); + + TEST_RESULT_VOID(cmdStoragePush(), "push file"); + TEST_RESULT_LOG("P00 INFO: push file path/aaa.txt to the archive."); + TEST_STORAGE_LIST(storageRepo(), "testStanza01/20260201-173010F", "aaa.txt\n", .comment = "check path exists and file added"); + + TEST_STORAGE_GET(storageRepo(), "testStanza01/20260201-173010F/aaa.txt", TEST_DATA); + + manifest = manifestLoadFile( + storageRepo(), STR(STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE), cipherTypeNone, NULL); + + /* Check manifest record */ + TEST_RESULT_STR_Z( + testManifestCustomFilesValidate(manifest), + "aaa.txt 445 445\n", + "compare file list"); + + TEST_TITLE("push compressed file"); + argList = strLstNew(); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, TEST_PATH "/bogus"); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, TEST_PATH "/repo"); + hrnCfgArgRawZ(argList, cfgOptStanza, TEST_STANZA); + hrnCfgArgRawZ(argList, cfgOptSet, TEST_BACKUP_LABEL_FULL); + hrnCfgArgRawZ(argList, cfgOptRepo, "2"); + hrnCfgArgRawZ(argList, cfgOptCompressType, "gz"); + hrnCfgArgRawZ(argList, cfgOptCompressLevel, "3"); strLstAddZ(argList, "path/aaa.txt"); HRN_INFO_PUT( @@ -1042,16 +1113,32 @@ testRun(void) TEST_RESULT_VOID(cmdStoragePush(), "push file"); TEST_RESULT_LOG("P00 INFO: push file path/aaa.txt to the archive."); - TEST_STORAGE_LIST(storageRepo(), "testStanza01/20260201-173010F", "aaa.txt\n", .comment = "check path exists and file added"); + TEST_STORAGE_LIST(storageRepo(), "testStanza01/20260201-173010F", "aaa.txt\naaa.txt.gz\n", .comment = "check path exists and file added"); - TEST_STORAGE_GET(storageRepo(), "testStanza01/20260201-173010F/aaa.txt", "TESTDATA"); + TEST_STORAGE_GET(storageRepo(), "testStanza01/20260201-173010F/aaa.txt", TEST_DATA, .compressType = compressTypeGz); + + manifest = manifestLoadFile( + storageRepo(), STR(STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE), cipherTypeNone, NULL); /* Check manifest record */ TEST_RESULT_STR_Z( - testManifestCustomFilesValidate(storageRepo(), STRDEF(STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL)), - "aaa.txt\n", + testManifestCustomFilesValidate(manifest), + "aaa.txt 445 445\naaa.txt.gz 283 445\n", "compare file list"); - TEST_TITLE("push compressed file"); + + TEST_TITLE("push encrypted file not supported"); + argList = strLstNew(); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, TEST_PATH "/bogus"); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, TEST_PATH "/repo"); + hrnCfgArgRawZ(argList, cfgOptStanza, TEST_STANZA); + hrnCfgArgRawZ(argList, cfgOptSet, TEST_BACKUP_LABEL_FULL); + hrnCfgArgRawZ(argList, cfgOptRepo, "2"); + hrnCfgArgRawZ(argList, cfgOptCipherPass, "unimportant"); + strLstAddZ(argList, "path/aaa.txt"); + + TEST_ERROR( + HRN_CFG_LOAD(cfgCmdRepoPush, argList), OptionInvalidError, + "option 'cipher-pass' not valid for command 'repo-push'"); } FUNCTION_HARNESS_RETURN_VOID(); From 0024bb6a97f397954d238b078ca97046a2090cbb Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Wed, 4 Feb 2026 23:10:07 +0300 Subject: [PATCH 06/21] changes from review --- src/command/repo/push.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command/repo/push.c b/src/command/repo/push.c index 5f1e16e5fe..0a5c6da93f 100644 --- a/src/command/repo/push.c +++ b/src/command/repo/push.c @@ -90,7 +90,7 @@ storagePushProcess(const String *file, CompressType compressType, int compressLe { ioFilterGroupAdd( writeFilterGroup, - compressFilterP(compressType, cfgOptionInt(cfgOptCompressLevel))); + compressFilterP(compressType, compressLevel)); repoChecksum = true; } @@ -190,7 +190,7 @@ cmdStoragePush(void) if (strLstSize(params) != 1) THROW(ParamInvalidError, "file parameter is required"); - String *filename = strLstGet(cfgCommandParam(), 0); + String *filename = strLstGet(params, 0); LOG_INFO_FMT( "push file %s to the archive.", From e020e95d7da0f557c548140071afc74b1ecd1be5 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 5 Feb 2026 08:20:29 +0300 Subject: [PATCH 07/21] Fix format --- test/src/module/command/repoTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/module/command/repoTest.c b/test/src/module/command/repoTest.c index 4dd6ff1bc0..607fd7844d 100644 --- a/test/src/module/command/repoTest.c +++ b/test/src/module/command/repoTest.c @@ -34,7 +34,7 @@ testManifestCustomFilesValidate(Manifest *manifest) const ManifestFile file = manifestFileUnpack(manifest, manifestCustomFilePackGet(manifest, fileIdx)); // strCatFmt(result, "%s\n", strZ(strLstGet(manifestFileList, manifestFileIdx))); - strCatFmt(result, "%s %lu %lu\n", strZ(file.name), file.size, file.sizeOriginal); + strCatFmt(result, "%s %" PRIu64 " %" PRIu64 "\n", strZ(file.name), file.size, file.sizeOriginal); } } MEM_CONTEXT_TEMP_END(); From 0afd9bd3ec1873aa384f077fee44fca55693e39f Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 5 Feb 2026 08:26:02 +0300 Subject: [PATCH 08/21] Remove comressed size from the test as it vary on different platforms --- test/src/module/command/repoTest.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/src/module/command/repoTest.c b/test/src/module/command/repoTest.c index 607fd7844d..c1fe9eefe0 100644 --- a/test/src/module/command/repoTest.c +++ b/test/src/module/command/repoTest.c @@ -34,7 +34,7 @@ testManifestCustomFilesValidate(Manifest *manifest) const ManifestFile file = manifestFileUnpack(manifest, manifestCustomFilePackGet(manifest, fileIdx)); // strCatFmt(result, "%s\n", strZ(strLstGet(manifestFileList, manifestFileIdx))); - strCatFmt(result, "%s %" PRIu64 " %" PRIu64 "\n", strZ(file.name), file.size, file.sizeOriginal); + strCatFmt(result, "%s %" PRIu64 "\n", strZ(file.name), file.sizeOriginal); } } MEM_CONTEXT_TEMP_END(); @@ -1055,7 +1055,7 @@ testRun(void) /* Check manifest record */ TEST_RESULT_STR_Z( testManifestCustomFilesValidate(manifest), - "aaa.txt 8 8\n", + "aaa.txt 8\n", "compare file list"); TEST_TITLE("push uncompressed file, replace existing"); @@ -1074,7 +1074,7 @@ testRun(void) /* Check manifest record */ TEST_RESULT_STR_Z( testManifestCustomFilesValidate(manifest), - "aaa.txt 445 445\n", + "aaa.txt 445\n", "compare file list"); TEST_TITLE("push compressed file"); @@ -1123,7 +1123,7 @@ testRun(void) /* Check manifest record */ TEST_RESULT_STR_Z( testManifestCustomFilesValidate(manifest), - "aaa.txt 445 445\naaa.txt.gz 283 445\n", + "aaa.txt 445\naaa.txt.gz 445\n", "compare file list"); TEST_TITLE("push encrypted file not supported"); From 1b42983245e66c47ce8f667c65de5ffb9b72a137 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 5 Feb 2026 12:21:06 +0300 Subject: [PATCH 09/21] Optimization, formatting, new test cases and check for ending slash in file name --- src/build/config/config.yaml | 3 +-- src/command/repo/push.c | 6 ++++- src/info/manifest.c | 10 ++++----- test/src/module/command/repoTest.c | 36 ++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/build/config/config.yaml b/src/build/config/config.yaml index aa8a77d250..1636c2ccd1 100644 --- a/src/build/config/config.yaml +++ b/src/build/config/config.yaml @@ -650,7 +650,7 @@ option: required: false command: repo-get: {} - repo-put: {} + repo-put: {} command-role: main: {} @@ -680,7 +680,6 @@ option: default: false command: repo-get: {} - repo-push: {} repo-put: {} command-role: main: {} diff --git a/src/command/repo/push.c b/src/command/repo/push.c index 0a5c6da93f..546a0654e7 100644 --- a/src/command/repo/push.c +++ b/src/command/repo/push.c @@ -144,7 +144,7 @@ storagePushProcess(const String *file, CompressType compressType, int compressLe for (unsigned int fileIdx = 0; fileIdx < manifestCustomFileTotal(manifest); fileIdx++) { - ManifestFile manifestFile = manifestFileUnpack(manifest, manifestCustomFilePackGet(manifest, fileIdx)); + ManifestFile manifestFile = manifestCustomFile(manifest, fileIdx); if (strCmp(manifestFile.name, destFilename) == 0){ manifestFile.mode = customFile.mode; @@ -192,6 +192,10 @@ cmdStoragePush(void) String *filename = strLstGet(params, 0); + // Check that the filename does not end with a slash + if (strEndsWith(filename, FSLASH_STR)) + THROW(ParamInvalidError, "file parameter should not end with a slash"); + LOG_INFO_FMT( "push file %s to the archive.", strZ(filename)); diff --git a/src/info/manifest.c b/src/info/manifest.c index bd87af39a7..9759d7c3d8 100644 --- a/src/info/manifest.c +++ b/src/info/manifest.c @@ -2045,7 +2045,8 @@ manifestOwnerDefaultGet(const Variant *const ownerDefault) FUNCTION_TEST_RETURN_CONST(VARIANT, varDup(ownerDefault)); } -static void manifestLoadFileEntry(Manifest *const manifest, ManifestLoadData *const loadData, const String *const key, const String *const value, ManifestFile *file) +static void +manifestLoadFileEntry(Manifest *const manifest, ManifestLoadData *const loadData, const String *const key, const String *const value, ManifestFile *file) { JsonRead *const json = jsonReadNew(value); jsonReadObjectBegin(json); @@ -2566,8 +2567,8 @@ manifestOwnerVar(const String *const ownerDefault) FUNCTION_TEST_RETURN_CONST(VARIANT, ownerDefault == NULL ? BOOL_FALSE_VAR : varNewStr(ownerDefault)); } - -static void manifestSaveFileEntry(const ManifestFile *file, ManifestSaveData *const saveData, const char *section, InfoSave *const infoSaveData) +static void +manifestSaveFileEntry(const ManifestFile *file, ManifestSaveData *const saveData, const char *section, InfoSave *const infoSaveData) { JsonWrite *const json = jsonWriteObjectBegin(jsonWriteNewP()); @@ -2642,7 +2643,6 @@ static void manifestSaveFileEntry(const ManifestFile *file, ManifestSaveData *co infoSaveValue( infoSaveData, section, strZ(file->name), jsonWriteResult(jsonWriteObjectEnd(json))); - } static void @@ -2927,7 +2927,7 @@ manifestSaveCallback(void *const callbackData, const String *const sectionNext, for (unsigned int fileIdx = 0; fileIdx < manifestFileTotal(manifest); fileIdx++) { const ManifestFile file = manifestFile(manifest, fileIdx); - + manifestSaveFileEntry(&file, saveData, MANIFEST_SECTION_TARGET_FILE, infoSaveData); MEM_CONTEXT_TEMP_RESET(1000); diff --git a/test/src/module/command/repoTest.c b/test/src/module/command/repoTest.c index c1fe9eefe0..d6c64259a9 100644 --- a/test/src/module/command/repoTest.c +++ b/test/src/module/command/repoTest.c @@ -1030,6 +1030,42 @@ testRun(void) cmdStoragePush(), ParamInvalidError, "file parameter is required"); + TEST_TITLE("invalid file"); + + argList = strLstNew(); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, TEST_PATH "/bogus"); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, TEST_PATH "/repo"); + hrnCfgArgRawZ(argList, cfgOptCompressType, "none"); + hrnCfgArgRawZ(argList, cfgOptStanza, TEST_STANZA); + hrnCfgArgRawZ(argList, cfgOptSet, TEST_BACKUP_LABEL_FULL); + hrnCfgArgRawZ(argList, cfgOptRepo, "2"); + strLstAddZ(argList, "path/aaa.txt/"); + + HRN_CFG_LOAD(cfgCmdRepoPush, argList); + + TEST_ERROR( + cmdStoragePush(), ParamInvalidError, + "file parameter should not end with a slash"); + + TEST_TITLE("non-existant file"); + + argList = strLstNew(); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, TEST_PATH "/bogus"); + hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, TEST_PATH "/repo"); + hrnCfgArgRawZ(argList, cfgOptCompressType, "none"); + hrnCfgArgRawZ(argList, cfgOptStanza, TEST_STANZA); + hrnCfgArgRawZ(argList, cfgOptSet, TEST_BACKUP_LABEL_FULL); + hrnCfgArgRawZ(argList, cfgOptRepo, "2"); + strLstAddZ(argList, "path/non-existant.txt"); + + HRN_CFG_LOAD(cfgCmdRepoPush, argList); + + TEST_ERROR( + cmdStoragePush(), FileMissingError, + "unable to open missing file '" TEST_PATH "/path/non-existant.txt' for read"); + + TEST_RESULT_LOG("P00 INFO: push file path/non-existant.txt to the archive."); + TEST_TITLE("push uncompressed file"); argList = strLstNew(); From 0c29416eabd5830282369c6e2dd04e5c829475cb Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 5 Feb 2026 17:04:41 +0300 Subject: [PATCH 10/21] Add unit tests --- test/src/module/common/typeStringTest.c | 7 ++- test/src/module/info/manifestTest.c | 80 +++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/test/src/module/common/typeStringTest.c b/test/src/module/common/typeStringTest.c index 927feada00..588c4faf29 100644 --- a/test/src/module/common/typeStringTest.c +++ b/test/src/module/common/typeStringTest.c @@ -120,7 +120,7 @@ testRun(void) } // ***************************************************************************************************************************** - if (testBegin("strBase(), strPath(), and strPathAbsolute()")) + if (testBegin("strBase(), strPath(), strPathAbsolute() and strFileName()")) { TEST_RESULT_STR_Z(strBase(STRDEF("")), "", "empty string"); TEST_RESULT_STR_Z(strBase(STRDEF("/")), "", "/ only"); @@ -143,6 +143,11 @@ testRun(void) TEST_RESULT_STR_Z(strPathAbsolute(STRDEF("../"), STRDEF("/path1")), "/", "simple relative path with trailing /"); TEST_RESULT_STR_Z( strPathAbsolute(STRDEF("../path2/.././path3"), STRDEF("/base1/base2")), "/base1/path3", "complex relative path"); + + TEST_RESULT_STR_Z(strFileName(STRDEF("/path/to/file")), "file", "filename"); + TEST_RESULT_STR_Z(strFileName(STRDEF("/path/to/file.ext")), "file.ext", "filename with extension"); + TEST_RESULT_STR_Z(strFileName(STRDEF("/path/to/file.ext/")), "", "empty filename if trailing /"); + TEST_RESULT_STR_Z(strFileName(STRDEF("")), "", "empty string"); } // ***************************************************************************************************************************** diff --git a/test/src/module/info/manifestTest.c b/test/src/module/info/manifestTest.c index e798082192..3cd923fa09 100644 --- a/test/src/module/info/manifestTest.c +++ b/test/src/module/info/manifestTest.c @@ -2047,4 +2047,84 @@ testRun(void) TEST_RESULT_VOID(manifestFree(manifest), "free manifest"); TEST_RESULT_VOID(manifestFree(NULL), "free null manifest"); } + + // ***************************************************************************************************************************** + if (testBegin("manifestCustomFileAdd(), manifestCustomFileUpdate() and manifestLinkAdd()")) + { + Manifest *manifest = NULL; + + HRN_STORAGE_PATH_CREATE(storageTest, "pg", .mode = 0700); + Storage *storagePg = storagePosixNewP(STRDEF(TEST_PATH "/pg")); + + // Add a tablespace to tablespaceList that does exist + PackWrite *tablespaceList = pckWriteNewP(); + + pckWriteArrayBeginP(tablespaceList); + pckWriteU32P(tablespaceList, 2); + pckWriteStrP(tablespaceList, STRDEF("tblspc2")); + pckWriteArrayEndP(tablespaceList); + pckWriteArrayBeginP(tablespaceList); + pckWriteU32P(tablespaceList, 1); + pckWriteStrP(tablespaceList, STRDEF("tblspc1")); + pckWriteArrayEndP(tablespaceList); + pckWriteEndP(tablespaceList); + + TEST_ASSIGN( + manifest, + manifestNewBuild( + storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, false, false, false, NULL, NULL, + pckWriteResult(tablespaceList)), + "build manifest"); + + #define TEST_CUSTOM_FILE_NAME STRDEF("somefile.txt") + + ManifestFile customFile = + { + .name = TEST_CUSTOM_FILE_NAME, + .mode = 0600, + .size = 42, + .sizeOriginal = 43, + .sizeRepo = 44, + .timestamp = time(NULL), + .checksumSha1 = bufPtr(bufNewDecode(encodingHex, STR("deadbeef5e6b4b0d3255bfef95601890afd80709"))), + }; + + TEST_RESULT_VOID(manifestCustomFileAdd(manifest, &customFile), "manifestCustomFileAdd"); + TEST_RESULT_UINT(manifestCustomFileTotal(manifest), 1, "manifestCustomFileTotal"); + + ManifestFilePack **const filePack = manifestCustomFilePackFindInternal(manifest, TEST_CUSTOM_FILE_NAME); + TEST_RESULT_PTR_NE(filePack, NULL, "file in the manifest"); + + ManifestFile file = manifestFileUnpack(manifest, *filePack); + + TEST_RESULT_UINT(file.size, 42, "size"); + + ManifestFile manifestFile = manifestCustomFile(manifest, 0); + + manifestFile.size = 45; + TEST_RESULT_VOID(manifestCustomFileUpdate(manifest, &manifestFile), "update"); + TEST_RESULT_UINT(manifestCustomFileTotal(manifest), 1, "manifestCustomFileTotal"); + + ManifestFile manifestFile2 = manifestCustomFile(manifest, 0); + TEST_RESULT_UINT(manifestFile2.size, 45, "size"); + + Buffer *contentSave = bufNew(0); + TEST_RESULT_VOID(manifestSave(manifest, ioBufferWriteNew(contentSave)), "save manifest"); + + Manifest *manifest2 = NULL; + + TEST_ASSIGN( + manifest2, + manifestNewLoad(ioBufferReadNew(contentSave)), + "load manifest" + ); + TEST_RESULT_PTR_NE(manifest2, NULL, "manifest loaded"); + TEST_RESULT_UINT(manifestCustomFileTotal(manifest2), 1, "manifestCustomFileTotal"); + manifestFile2 = manifestCustomFile(manifest2, 0); + TEST_RESULT_UINT(manifestFile2.size, 45, "size"); + + // Try to find non-existent file + TEST_ERROR(manifestCustomFilePackFindInternal(manifest2, STRDEF("badbadfile.txt")), + AssertError, "unable to find 'badbadfile.txt' in manifest file list"); + } } From 650fb05c88890fe60534e0d9d17014b13b63e604 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 5 Feb 2026 17:11:24 +0300 Subject: [PATCH 11/21] fix typo --- test/src/module/command/repoTest.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/src/module/command/repoTest.c b/test/src/module/command/repoTest.c index d6c64259a9..b3e7a5f6dd 100644 --- a/test/src/module/command/repoTest.c +++ b/test/src/module/command/repoTest.c @@ -1047,7 +1047,7 @@ testRun(void) cmdStoragePush(), ParamInvalidError, "file parameter should not end with a slash"); - TEST_TITLE("non-existant file"); + TEST_TITLE("non-existent file"); argList = strLstNew(); hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, TEST_PATH "/bogus"); @@ -1056,15 +1056,15 @@ testRun(void) hrnCfgArgRawZ(argList, cfgOptStanza, TEST_STANZA); hrnCfgArgRawZ(argList, cfgOptSet, TEST_BACKUP_LABEL_FULL); hrnCfgArgRawZ(argList, cfgOptRepo, "2"); - strLstAddZ(argList, "path/non-existant.txt"); + strLstAddZ(argList, "path/non-existent.txt"); HRN_CFG_LOAD(cfgCmdRepoPush, argList); TEST_ERROR( cmdStoragePush(), FileMissingError, - "unable to open missing file '" TEST_PATH "/path/non-existant.txt' for read"); + "unable to open missing file '" TEST_PATH "/path/non-existent.txt' for read"); - TEST_RESULT_LOG("P00 INFO: push file path/non-existant.txt to the archive."); + TEST_RESULT_LOG("P00 INFO: push file path/non-existent.txt to the archive."); TEST_TITLE("push uncompressed file"); From 906d35ed6dc23c5ff6b9c258963ae7cab876d00f Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 5 Feb 2026 17:20:14 +0300 Subject: [PATCH 12/21] tests number --- test/define.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/define.yaml b/test/define.yaml index 70da67be02..fbcab5fbbb 100644 --- a/test/define.yaml +++ b/test/define.yaml @@ -811,7 +811,7 @@ unit: # ---------------------------------------------------------------------------------------------------------------------------- - name: manifest - total: 6 + total: 7 harness: name: manifest shim: From 3810fab2ce9a7eb3c254547b9876b508c4f53d21 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 5 Feb 2026 17:44:56 +0300 Subject: [PATCH 13/21] typo --- test/src/module/info/manifestTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/module/info/manifestTest.c b/test/src/module/info/manifestTest.c index 3cd923fa09..926bd4181d 100644 --- a/test/src/module/info/manifestTest.c +++ b/test/src/module/info/manifestTest.c @@ -2049,7 +2049,7 @@ testRun(void) } // ***************************************************************************************************************************** - if (testBegin("manifestCustomFileAdd(), manifestCustomFileUpdate() and manifestLinkAdd()")) + if (testBegin("manifestCustomFileAdd(), manifestCustomFileUpdate() and manifestSave()/manifestNewLoad()")) { Manifest *manifest = NULL; From 240089baeb924ffc3669bfa4bfcfb1440490ab58 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 5 Feb 2026 18:01:49 +0300 Subject: [PATCH 14/21] try to use ubuntu 22 for documentation build --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 10a8eaa765..80ec101fa9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,7 +35,7 @@ jobs: - param: test --vm=d11 --param=no-performance --param=no-coverage --param=no-valgrind # Debian/Ubuntu documentation - - param: doc --vm=u20 + - param: doc --vm=u22 # All integration tests - param: test --vm=u22 --param=build-package --param=module=integration From 979840c238312b407cf5993fdc15645952bf8e28 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 5 Feb 2026 18:17:21 +0300 Subject: [PATCH 15/21] try to suppress Azurite API version checking in documentation builds. --- doc/xml/user-guide.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/xml/user-guide.xml b/doc/xml/user-guide.xml index b6d9fbdf44..ef9c89c731 100644 --- a/doc/xml/user-guide.xml +++ b/doc/xml/user-guide.xml @@ -905,7 +905,7 @@ Introduction - + From 6737811117457872e44ae5c15b4ad98979301a14 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 5 Feb 2026 22:48:07 +0300 Subject: [PATCH 16/21] rollback --- .github/workflows/test.yml | 2 +- doc/xml/user-guide.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 80ec101fa9..10a8eaa765 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,7 +35,7 @@ jobs: - param: test --vm=d11 --param=no-performance --param=no-coverage --param=no-valgrind # Debian/Ubuntu documentation - - param: doc --vm=u22 + - param: doc --vm=u20 # All integration tests - param: test --vm=u22 --param=build-package --param=module=integration diff --git a/doc/xml/user-guide.xml b/doc/xml/user-guide.xml index ef9c89c731..b6d9fbdf44 100644 --- a/doc/xml/user-guide.xml +++ b/doc/xml/user-guide.xml @@ -905,7 +905,7 @@ Introduction - + From ff2dea086a4c0f95e6390c3c3c5f534ec0e347ff Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Fri, 6 Feb 2026 12:57:22 +0300 Subject: [PATCH 17/21] change upload path due to SRS update --- src/command/repo/push.c | 2 +- test/src/module/command/repoTest.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/command/repo/push.c b/src/command/repo/push.c index 546a0654e7..bea700e928 100644 --- a/src/command/repo/push.c +++ b/src/command/repo/push.c @@ -27,7 +27,7 @@ composeDestinationPath(const String *stanza, const String *backupLabel, const St FUNCTION_LOG_PARAM(STRING, fileName); FUNCTION_LOG_END(); - String *const result = strNewFmt("%s/%s/%s", strZ(stanza), strZ(backupLabel), strZ(fileName)); + String *const result = strNewFmt(STORAGE_PATH_BACKUP "/%s/%s/%s", strZ(stanza), strZ(backupLabel), strZ(fileName)); FUNCTION_LOG_RETURN(STRING, result); } diff --git a/test/src/module/command/repoTest.c b/test/src/module/command/repoTest.c index b3e7a5f6dd..eedcc87abd 100644 --- a/test/src/module/command/repoTest.c +++ b/test/src/module/command/repoTest.c @@ -1081,9 +1081,9 @@ testRun(void) TEST_RESULT_VOID(cmdStoragePush(), "push file"); TEST_RESULT_LOG("P00 INFO: push file path/aaa.txt to the archive."); - TEST_STORAGE_LIST(storageRepo(), "testStanza01/20260201-173010F", "aaa.txt\n", .comment = "check path exists and file added"); + TEST_STORAGE_LIST(storageRepo(), STORAGE_PATH_BACKUP "/" TEST_STANZA "/" TEST_BACKUP_LABEL_FULL, "aaa.txt\nbackup.manifest\n", .comment = "check path exists and file added"); - TEST_STORAGE_GET(storageRepo(), "testStanza01/20260201-173010F/aaa.txt", "TESTDATA"); + TEST_STORAGE_GET(storageRepo(), STORAGE_PATH_BACKUP "/" TEST_STANZA "/" TEST_BACKUP_LABEL_FULL "/aaa.txt", "TESTDATA"); Manifest *manifest = manifestLoadFile( storageRepo(), STR(STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE), cipherTypeNone, NULL); @@ -1100,9 +1100,9 @@ testRun(void) TEST_RESULT_VOID(cmdStoragePush(), "push file"); TEST_RESULT_LOG("P00 INFO: push file path/aaa.txt to the archive."); - TEST_STORAGE_LIST(storageRepo(), "testStanza01/20260201-173010F", "aaa.txt\n", .comment = "check path exists and file added"); + TEST_STORAGE_LIST(storageRepo(), STORAGE_PATH_BACKUP "/" TEST_STANZA "/" TEST_BACKUP_LABEL_FULL, "aaa.txt\nbackup.manifest\n", .comment = "check path exists and file added"); - TEST_STORAGE_GET(storageRepo(), "testStanza01/20260201-173010F/aaa.txt", TEST_DATA); + TEST_STORAGE_GET(storageRepo(), STORAGE_PATH_BACKUP "/" TEST_STANZA "/" TEST_BACKUP_LABEL_FULL "/aaa.txt", TEST_DATA); manifest = manifestLoadFile( storageRepo(), STR(STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE), cipherTypeNone, NULL); @@ -1149,9 +1149,9 @@ testRun(void) TEST_RESULT_VOID(cmdStoragePush(), "push file"); TEST_RESULT_LOG("P00 INFO: push file path/aaa.txt to the archive."); - TEST_STORAGE_LIST(storageRepo(), "testStanza01/20260201-173010F", "aaa.txt\naaa.txt.gz\n", .comment = "check path exists and file added"); + TEST_STORAGE_LIST(storageRepo(), STORAGE_PATH_BACKUP "/" TEST_STANZA "/" TEST_BACKUP_LABEL_FULL, "aaa.txt\naaa.txt.gz\nbackup.manifest\n", .comment = "check path exists and file added"); - TEST_STORAGE_GET(storageRepo(), "testStanza01/20260201-173010F/aaa.txt", TEST_DATA, .compressType = compressTypeGz); + TEST_STORAGE_GET(storageRepo(), STORAGE_PATH_BACKUP "/" TEST_STANZA "/" TEST_BACKUP_LABEL_FULL "/aaa.txt", TEST_DATA, .compressType = compressTypeGz); manifest = manifestLoadFile( storageRepo(), STR(STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE), cipherTypeNone, NULL); From 57da285bb1ee7423055b8c68e78a4ba06e2a8182 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Tue, 17 Feb 2026 13:44:33 +0300 Subject: [PATCH 18/21] forgotten comment --- test/src/module/command/repoTest.c | 1 - 1 file changed, 1 deletion(-) diff --git a/test/src/module/command/repoTest.c b/test/src/module/command/repoTest.c index eedcc87abd..d3fd47324a 100644 --- a/test/src/module/command/repoTest.c +++ b/test/src/module/command/repoTest.c @@ -33,7 +33,6 @@ testManifestCustomFilesValidate(Manifest *manifest) { const ManifestFile file = manifestFileUnpack(manifest, manifestCustomFilePackGet(manifest, fileIdx)); - // strCatFmt(result, "%s\n", strZ(strLstGet(manifestFileList, manifestFileIdx))); strCatFmt(result, "%s %" PRIu64 "\n", strZ(file.name), file.sizeOriginal); } } From b818d957ac391da32305120e9287c0bda3d01f18 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Tue, 17 Feb 2026 13:45:38 +0300 Subject: [PATCH 19/21] fix --- src/build/help/help.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build/help/help.xml b/src/build/help/help.xml index 4150623d20..bb312d7238 100644 --- a/src/build/help/help.xml +++ b/src/build/help/help.xml @@ -2414,7 +2414,7 @@ - + Upload a metadata file to the repository. From d16d344e3df262d6dc05b780195820868e70da9a Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Wed, 18 Feb 2026 18:06:58 +0300 Subject: [PATCH 20/21] Change error message --- src/command/repo/push.c | 2 +- test/src/module/command/repoTest.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command/repo/push.c b/src/command/repo/push.c index bea700e928..7e68440ede 100644 --- a/src/command/repo/push.c +++ b/src/command/repo/push.c @@ -188,7 +188,7 @@ cmdStoragePush(void) const StringList *params = cfgCommandParam(); if (strLstSize(params) != 1) - THROW(ParamInvalidError, "file parameter is required"); + THROW(ParamInvalidError, "exactly one parameter is required"); String *filename = strLstGet(params, 0); diff --git a/test/src/module/command/repoTest.c b/test/src/module/command/repoTest.c index d3fd47324a..b1a8a2e6e6 100644 --- a/test/src/module/command/repoTest.c +++ b/test/src/module/command/repoTest.c @@ -1027,7 +1027,7 @@ testRun(void) TEST_ERROR( cmdStoragePush(), ParamInvalidError, - "file parameter is required"); + "exactly one parameter is required"); TEST_TITLE("invalid file"); From 09d3abc0f3142e5901cd2d5c64e57ec6d69e8abc Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Wed, 11 Mar 2026 17:59:00 +0300 Subject: [PATCH 21/21] Use to build path --- src/command/repo/push.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/command/repo/push.c b/src/command/repo/push.c index 7e68440ede..720b8e6871 100644 --- a/src/command/repo/push.c +++ b/src/command/repo/push.c @@ -19,15 +19,14 @@ Repository Put Command #include "storage/helper.h" static String * -composeDestinationPath(const String *stanza, const String *backupLabel, const String *fileName) +composeDestinationPath(const String *backupLabel, const String *fileName) { FUNCTION_LOG_BEGIN(logLevelDebug); - FUNCTION_LOG_PARAM(STRING, stanza); FUNCTION_LOG_PARAM(STRING, backupLabel); FUNCTION_LOG_PARAM(STRING, fileName); FUNCTION_LOG_END(); - String *const result = strNewFmt(STORAGE_PATH_BACKUP "/%s/%s/%s", strZ(stanza), strZ(backupLabel), strZ(fileName)); + String *const result = strNewFmt(STORAGE_REPO_BACKUP "/%s/%s", strZ(backupLabel), strZ(fileName)); FUNCTION_LOG_RETURN(STRING, result); } @@ -56,13 +55,12 @@ storagePushProcess(const String *file, CompressType compressType, int compressLe // Repository Path Formation - const String *stanza = cfgOptionStr(cfgOptStanza); const String *backupLabel = cfgOptionStr(cfgOptSet); String *destFilename = strCat(strNew(), strFileName(file)); compressExtCat(destFilename, compressType); - String *destPath = composeDestinationPath(stanza, backupLabel, destFilename); + String *destPath = composeDestinationPath(backupLabel, destFilename); // Is path valid for repo? destPath = repoPathIsValid(destPath);