|
| 1 | +/*********************************************************************************************************************************** |
| 2 | +Repository Put Command |
| 3 | +***********************************************************************************************************************************/ |
| 4 | +#include "build.auto.h" |
| 5 | + |
| 6 | +#include <unistd.h> |
| 7 | + |
| 8 | +#include "command/repo/common.h" |
| 9 | +#include "common/compress/helper.h" |
| 10 | +#include "common/debug.h" |
| 11 | +#include "common/io/fdRead.h" |
| 12 | +#include "common/io/filter/size.h" |
| 13 | +#include "common/io/io.h" |
| 14 | +#include "common/log.h" |
| 15 | +#include "common/memContext.h" |
| 16 | +#include "common/type/string.h" |
| 17 | +#include "config/config.h" |
| 18 | +#include "info/manifest.h" |
| 19 | +#include "storage/helper.h" |
| 20 | + |
| 21 | +static String * |
| 22 | +composeDestinationPath(const String *backupLabel, const String *fileName) |
| 23 | +{ |
| 24 | + FUNCTION_LOG_BEGIN(logLevelDebug); |
| 25 | + FUNCTION_LOG_PARAM(STRING, backupLabel); |
| 26 | + FUNCTION_LOG_PARAM(STRING, fileName); |
| 27 | + FUNCTION_LOG_END(); |
| 28 | + |
| 29 | + String *const result = strNewFmt(STORAGE_REPO_BACKUP "/%s/%s", strZ(backupLabel), strZ(fileName)); |
| 30 | + |
| 31 | + FUNCTION_LOG_RETURN(STRING, result); |
| 32 | +} |
| 33 | + |
| 34 | +/*********************************************************************************************************************************** |
| 35 | +Write source IO to destination file |
| 36 | +***********************************************************************************************************************************/ |
| 37 | +static void |
| 38 | +storagePushProcess(const String *file, CompressType compressType, int compressLevel) |
| 39 | +{ |
| 40 | + FUNCTION_LOG_BEGIN(logLevelDebug); |
| 41 | + FUNCTION_LOG_PARAM(STRING, file); |
| 42 | + FUNCTION_LOG_PARAM(ENUM, compressType); |
| 43 | + FUNCTION_LOG_PARAM(INT, compressLevel); |
| 44 | + FUNCTION_LOG_END(); |
| 45 | + |
| 46 | + // Ensure that the file exists and readable |
| 47 | + MEM_CONTEXT_TEMP_BEGIN() |
| 48 | + { |
| 49 | + // Normalize source file path |
| 50 | + // Get current working dir |
| 51 | + char currentWorkDir[1024]; |
| 52 | + THROW_ON_SYS_ERROR(getcwd(currentWorkDir, sizeof(currentWorkDir)) == NULL, FormatError, "unable to get cwd"); |
| 53 | + |
| 54 | + String *sourcePath = strPathAbsolute(file, strNewZ(currentWorkDir)); |
| 55 | + |
| 56 | + // Repository Path Formation |
| 57 | + |
| 58 | + const String *backupLabel = cfgOptionStr(cfgOptSet); |
| 59 | + |
| 60 | + String *destFilename = strCat(strNew(), strFileName(file)); |
| 61 | + compressExtCat(destFilename, compressType); |
| 62 | + |
| 63 | + String *destPath = composeDestinationPath(backupLabel, destFilename); |
| 64 | + |
| 65 | + // Is path valid for repo? |
| 66 | + destPath = repoPathIsValid(destPath); |
| 67 | + |
| 68 | + bool repoChecksum = false; |
| 69 | + const Storage *storage = storageRepoWrite(); |
| 70 | + const StorageWrite *const destination = storageNewWriteP(storage, destPath); |
| 71 | + |
| 72 | + IoRead *const sourceIo = storageReadIo(storageNewReadP(storageLocal(), sourcePath)); |
| 73 | + IoWrite *const destinationIo = storageWriteIo(destination); |
| 74 | + |
| 75 | + IoFilterGroup *const readFilterGroup = ioReadFilterGroup(sourceIo); |
| 76 | + IoFilterGroup *const writeFilterGroup = ioWriteFilterGroup(destinationIo); |
| 77 | + |
| 78 | + const String *manifestFileName = strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strZ(backupLabel)); |
| 79 | + Manifest *manifest = manifestLoadFile( |
| 80 | + storage, manifestFileName, |
| 81 | + cipherTypeNone, NULL); |
| 82 | + |
| 83 | + // Add SHA1 filter |
| 84 | + ioFilterGroupAdd(writeFilterGroup, cryptoHashNew(hashTypeSha1)); |
| 85 | + |
| 86 | + // Add compression |
| 87 | + if (compressType != compressTypeNone) |
| 88 | + { |
| 89 | + ioFilterGroupAdd( |
| 90 | + writeFilterGroup, |
| 91 | + compressFilterP(compressType, compressLevel)); |
| 92 | + |
| 93 | + repoChecksum = true; |
| 94 | + } |
| 95 | + |
| 96 | + // Capture checksum of file stored in the repo if filters that modify the output have been applied |
| 97 | + if (repoChecksum) |
| 98 | + ioFilterGroupAdd(writeFilterGroup, cryptoHashNew(hashTypeSha1)); |
| 99 | + |
| 100 | + // Add size filter last to calculate repo size |
| 101 | + ioFilterGroupAdd(writeFilterGroup, ioSizeNew()); |
| 102 | + |
| 103 | + // Add size filter last to calculate source file size |
| 104 | + ioFilterGroupAdd(readFilterGroup, ioSizeNew()); |
| 105 | + |
| 106 | + // Open source and destination |
| 107 | + ioReadOpen(sourceIo); |
| 108 | + ioWriteOpen(destinationIo); |
| 109 | + |
| 110 | + // Copy data from source to destination |
| 111 | + ioCopyP(sourceIo, destinationIo); |
| 112 | + |
| 113 | + // Close the source and destination |
| 114 | + ioReadClose(sourceIo); |
| 115 | + ioWriteClose(destinationIo); |
| 116 | + |
| 117 | + // Use base path to set ownership and mode |
| 118 | + const ManifestPath *const basePath = manifestPathFind(manifest, MANIFEST_TARGET_PGDATA_STR); |
| 119 | + |
| 120 | + // Add to manifest |
| 121 | + uint64_t rsize = pckReadU64P(ioFilterGroupResultP(readFilterGroup, SIZE_FILTER_TYPE)); |
| 122 | + uint64_t wsize = pckReadU64P(ioFilterGroupResultP(writeFilterGroup, SIZE_FILTER_TYPE)); |
| 123 | + ManifestFile customFile = |
| 124 | + { |
| 125 | + .name = destFilename, |
| 126 | + .mode = basePath->mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), |
| 127 | + .size = wsize, |
| 128 | + .sizeOriginal = rsize, |
| 129 | + .sizeRepo = wsize, |
| 130 | + .timestamp = time(NULL), |
| 131 | + .checksumSha1 = bufPtr(pckReadBinP(ioFilterGroupResultP(writeFilterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 0))), |
| 132 | + }; |
| 133 | + |
| 134 | + if (repoChecksum) |
| 135 | + { |
| 136 | + PackRead *packRead = ioFilterGroupResultP(writeFilterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 1); |
| 137 | + ASSERT(packRead != NULL); |
| 138 | + customFile.checksumRepoSha1 = bufPtr(pckReadBinP(packRead)); |
| 139 | + } |
| 140 | + |
| 141 | + bool found = false; |
| 142 | + |
| 143 | + for (unsigned int fileIdx = 0; fileIdx < manifestCustomFileTotal(manifest); fileIdx++) |
| 144 | + { |
| 145 | + ManifestFile manifestFile = manifestCustomFile(manifest, fileIdx); |
| 146 | + |
| 147 | + if (strCmp(manifestFile.name, destFilename) == 0){ |
| 148 | + manifestFile.mode = customFile.mode; |
| 149 | + manifestFile.size = customFile.size; |
| 150 | + manifestFile.sizeOriginal = customFile.sizeOriginal; |
| 151 | + manifestFile.sizeRepo = customFile.sizeRepo; |
| 152 | + manifestFile.timestamp = customFile.timestamp; |
| 153 | + manifestFile.checksumSha1 = customFile.checksumSha1; |
| 154 | + |
| 155 | + manifestCustomFileUpdate(manifest, &manifestFile); |
| 156 | + found = true; |
| 157 | + break; |
| 158 | + } |
| 159 | + } |
| 160 | + |
| 161 | + if (!found) |
| 162 | + manifestCustomFileAdd(manifest, &customFile); |
| 163 | + |
| 164 | + // Save manifest |
| 165 | + IoWrite *const manifestWrite = storageWriteIo( |
| 166 | + storageNewWriteP( |
| 167 | + storageRepoWrite(), |
| 168 | + manifestFileName |
| 169 | + )); |
| 170 | + |
| 171 | + manifestSave(manifest, manifestWrite); |
| 172 | + } |
| 173 | + MEM_CONTEXT_TEMP_END(); |
| 174 | + |
| 175 | + FUNCTION_LOG_RETURN_VOID(); |
| 176 | +} |
| 177 | + |
| 178 | +/**********************************************************************************************************************************/ |
| 179 | +FN_EXTERN void |
| 180 | +cmdStoragePush(void) |
| 181 | +{ |
| 182 | + FUNCTION_LOG_VOID(logLevelDebug); |
| 183 | + |
| 184 | + MEM_CONTEXT_TEMP_BEGIN() |
| 185 | + { |
| 186 | + const StringList *params = cfgCommandParam(); |
| 187 | + |
| 188 | + if (strLstSize(params) != 1) |
| 189 | + THROW(ParamInvalidError, "exactly one parameter is required"); |
| 190 | + |
| 191 | + String *filename = strLstGet(params, 0); |
| 192 | + |
| 193 | + // Check that the filename does not end with a slash |
| 194 | + if (strEndsWith(filename, FSLASH_STR)) |
| 195 | + THROW(ParamInvalidError, "file parameter should not end with a slash"); |
| 196 | + |
| 197 | + LOG_INFO_FMT( |
| 198 | + "push file %s to the archive.", |
| 199 | + strZ(filename)); |
| 200 | + |
| 201 | + storagePushProcess(filename, |
| 202 | + compressTypeEnum(cfgOptionStrId(cfgOptCompressType)), |
| 203 | + cfgOptionInt(cfgOptCompressLevel)); |
| 204 | + } |
| 205 | + MEM_CONTEXT_TEMP_END(); |
| 206 | + |
| 207 | + FUNCTION_LOG_RETURN_VOID(); |
| 208 | +} |
0 commit comments