1 /***********************************************************************************************************************************
2 Backup Info Handler
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5 
6 #include <stdarg.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <inttypes.h>
10 
11 #include "command/backup/common.h"
12 #include "common/crypto/cipherBlock.h"
13 #include "common/debug.h"
14 #include "common/ini.h"
15 #include "common/io/bufferWrite.h"
16 #include "common/io/io.h"
17 #include "common/log.h"
18 #include "common/memContext.h"
19 #include "common/regExp.h"
20 #include "common/type/json.h"
21 #include "common/type/list.h"
22 #include "info/infoBackup.h"
23 #include "info/manifest.h"
24 #include "postgres/interface.h"
25 #include "postgres/version.h"
26 #include "storage/helper.h"
27 #include "version.h"
28 
29 /***********************************************************************************************************************************
30 Constants
31 ***********************************************************************************************************************************/
32 #define INFO_BACKUP_SECTION                                         "backup"
33 #define INFO_BACKUP_SECTION_BACKUP_CURRENT                          INFO_BACKUP_SECTION ":current"
34     STRING_STATIC(INFO_BACKUP_SECTION_BACKUP_CURRENT_STR,           INFO_BACKUP_SECTION_BACKUP_CURRENT);
35 
36 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_ARCHIVE_START_VAR,     "backup-archive-start");
37 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_ARCHIVE_STOP_VAR,      "backup-archive-stop");
38 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_INFO_REPO_SIZE_VAR,    "backup-info-repo-size");
39 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_INFO_REPO_SIZE_DELTA_VAR, "backup-info-repo-size-delta");
40 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_INFO_SIZE_VAR,         "backup-info-size");
41 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_INFO_SIZE_DELTA_VAR,   "backup-info-size-delta");
42 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_PRIOR_VAR,             "backup-prior");
43 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_REFERENCE_VAR,         "backup-reference");
44 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_TIMESTAMP_START_VAR,   "backup-timestamp-start");
45 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_TIMESTAMP_STOP_VAR,    "backup-timestamp-stop");
46 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_BACKUP_TYPE_VAR,              "backup-type");
47 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_OPT_ARCHIVE_CHECK_VAR,        "option-archive-check");
48 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_OPT_ARCHIVE_COPY_VAR,         "option-archive-copy");
49 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_OPT_BACKUP_STANDBY_VAR,       "option-backup-standby");
50 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_OPT_CHECKSUM_PAGE_VAR,        "option-checksum-page");
51 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_OPT_COMPRESS_VAR,             "option-compress");
52 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_OPT_HARDLINK_VAR,             "option-hardlink");
53 VARIANT_STRDEF_STATIC(INFO_BACKUP_KEY_OPT_ONLINE_VAR,               "option-online");
54 
55 STRING_EXTERN(INFO_BACKUP_PATH_FILE_STR,                            INFO_BACKUP_PATH_FILE);
56 STRING_EXTERN(INFO_BACKUP_PATH_FILE_COPY_STR,                       INFO_BACKUP_PATH_FILE_COPY);
57 
58 /***********************************************************************************************************************************
59 Object type
60 ***********************************************************************************************************************************/
61 struct InfoBackup
62 {
63     InfoBackupPub pub;                                              // Publicly accessible variables
64 };
65 
66 /***********************************************************************************************************************************
67 Internal constructor
68 ***********************************************************************************************************************************/
69 static InfoBackup *
infoBackupNewInternal(void)70 infoBackupNewInternal(void)
71 {
72     FUNCTION_TEST_VOID();
73 
74     InfoBackup *this = memNew(sizeof(InfoBackup));
75 
76     *this = (InfoBackup)
77     {
78         .pub =
79         {
80             .memContext = memContextCurrent(),
81             .backup = lstNewP(sizeof(InfoBackupData), .comparator =  lstComparatorStr),
82         },
83     };
84 
85     FUNCTION_TEST_RETURN(this);
86 }
87 
88 /**********************************************************************************************************************************/
89 InfoBackup *
infoBackupNew(unsigned int pgVersion,uint64_t pgSystemId,unsigned int pgCatalogVersion,const String * cipherPassSub)90 infoBackupNew(unsigned int pgVersion, uint64_t pgSystemId, unsigned int pgCatalogVersion, const String *cipherPassSub)
91 {
92     FUNCTION_LOG_BEGIN(logLevelDebug);
93         FUNCTION_LOG_PARAM(UINT, pgVersion);
94         FUNCTION_LOG_PARAM(UINT64, pgSystemId);
95         FUNCTION_LOG_PARAM(UINT, pgCatalogVersion);
96         FUNCTION_TEST_PARAM(STRING, cipherPassSub);
97     FUNCTION_LOG_END();
98 
99     ASSERT(pgVersion > 0 && pgSystemId > 0 && pgCatalogVersion > 0);
100 
101     InfoBackup *this = NULL;
102 
103     MEM_CONTEXT_NEW_BEGIN("InfoBackup")
104     {
105         this = infoBackupNewInternal();
106 
107         // Initialize the pg data
108         this->pub.infoPg = infoPgNew(infoPgBackup, cipherPassSub);
109         infoBackupPgSet(this, pgVersion, pgSystemId, pgCatalogVersion);
110     }
111     MEM_CONTEXT_NEW_END();
112 
113     FUNCTION_LOG_RETURN(INFO_BACKUP, this);
114 }
115 
116 /***********************************************************************************************************************************
117 Create new object and load contents from a file
118 ***********************************************************************************************************************************/
119 static void
infoBackupLoadCallback(void * data,const String * section,const String * key,const Variant * value)120 infoBackupLoadCallback(void *data, const String *section, const String *key, const Variant *value)
121 {
122     FUNCTION_TEST_BEGIN();
123         FUNCTION_TEST_PARAM_P(VOID, data);
124         FUNCTION_TEST_PARAM(STRING, section);
125         FUNCTION_TEST_PARAM(STRING, key);
126         FUNCTION_TEST_PARAM(VARIANT, value);
127     FUNCTION_TEST_END();
128 
129     ASSERT(data != NULL);
130     ASSERT(section != NULL);
131     ASSERT(key != NULL);
132     ASSERT(value != NULL);
133 
134     InfoBackup *infoBackup = (InfoBackup *)data;
135 
136     // Process current backup list
137     if (strEq(section, INFO_BACKUP_SECTION_BACKUP_CURRENT_STR))
138     {
139         const KeyValue *backupKv = varKv(value);
140 
141         MEM_CONTEXT_BEGIN(lstMemContext(infoBackup->pub.backup))
142         {
143             InfoBackupData infoBackupData =
144             {
145                 .backrestFormat = varUIntForce(kvGet(backupKv, VARSTR(INFO_KEY_FORMAT_STR))),
146                 .backrestVersion = varStrForce(kvGet(backupKv, VARSTR(INFO_KEY_VERSION_STR))),
147                 .backupInfoRepoSize = varUInt64(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_INFO_REPO_SIZE_VAR)),
148                 .backupInfoRepoSizeDelta = varUInt64(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_INFO_REPO_SIZE_DELTA_VAR)),
149                 .backupInfoSize = varUInt64(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_INFO_SIZE_VAR)),
150                 .backupInfoSizeDelta = varUInt64(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_INFO_SIZE_DELTA_VAR)),
151                 .backupLabel = strDup(key),
152                 .backupPgId = cvtZToUInt(strZ(varStrForce(kvGet(backupKv, INFO_KEY_DB_ID_VAR)))),
153 
154                 // When reading timestamps, read as uint64 to ensure always positive value (guarantee no backups before 1970)
155                 .backupTimestampStart = (time_t)varUInt64(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_TIMESTAMP_START_VAR)),
156                 .backupTimestampStop= (time_t)varUInt64(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_TIMESTAMP_STOP_VAR)),
157                 .backupType = (BackupType)strIdFromStr(stringIdBit5, varStr(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_TYPE_VAR))),
158 
159                 // Possible NULL values
160                 .backupArchiveStart = strDup(varStr(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_ARCHIVE_START_VAR))),
161                 .backupArchiveStop = strDup(varStr(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_ARCHIVE_STOP_VAR))),
162                 .backupPrior = strDup(varStr(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_PRIOR_VAR))),
163                 .backupReference =
164                     kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_REFERENCE_VAR) != NULL ?
165                         strLstNewVarLst(varVarLst(kvGet(backupKv, INFO_BACKUP_KEY_BACKUP_REFERENCE_VAR))) : NULL,
166 
167                 // Options
168                 .optionArchiveCheck = varBool(kvGet(backupKv, INFO_BACKUP_KEY_OPT_ARCHIVE_CHECK_VAR)),
169                 .optionArchiveCopy = varBool(kvGet(backupKv, INFO_BACKUP_KEY_OPT_ARCHIVE_COPY_VAR)),
170                 .optionBackupStandby = varBool(kvGet(backupKv, INFO_BACKUP_KEY_OPT_BACKUP_STANDBY_VAR)),
171                 .optionChecksumPage = varBool(kvGet(backupKv, INFO_BACKUP_KEY_OPT_CHECKSUM_PAGE_VAR)),
172                 .optionCompress = varBool(kvGet(backupKv, INFO_BACKUP_KEY_OPT_COMPRESS_VAR)),
173                 .optionHardlink = varBool(kvGet(backupKv, INFO_BACKUP_KEY_OPT_HARDLINK_VAR)),
174                 .optionOnline = varBool(kvGet(backupKv, INFO_BACKUP_KEY_OPT_ONLINE_VAR)),
175             };
176 
177             ASSERT(
178                 infoBackupData.backupType == backupTypeFull || infoBackupData.backupType == backupTypeDiff ||
179                 infoBackupData.backupType == backupTypeIncr);
180 
181             // Add the backup data to the list
182             lstAdd(infoBackup->pub.backup, &infoBackupData);
183         }
184         MEM_CONTEXT_END();
185     }
186 
187     FUNCTION_TEST_RETURN_VOID();
188 }
189 
190 /**********************************************************************************************************************************/
191 InfoBackup *
infoBackupNewLoad(IoRead * read)192 infoBackupNewLoad(IoRead *read)
193 {
194     FUNCTION_LOG_BEGIN(logLevelDebug);
195         FUNCTION_LOG_PARAM(IO_READ, read);
196     FUNCTION_LOG_END();
197 
198     ASSERT(read != NULL);
199 
200     InfoBackup *this = NULL;
201 
202     MEM_CONTEXT_NEW_BEGIN("InfoBackup")
203     {
204         this = infoBackupNewInternal();
205         this->pub.infoPg = infoPgNewLoad(read, infoPgBackup, infoBackupLoadCallback, this);
206     }
207     MEM_CONTEXT_NEW_END();
208 
209     FUNCTION_LOG_RETURN(INFO_BACKUP, this);
210 }
211 
212 /***********************************************************************************************************************************
213 Save to file
214 ***********************************************************************************************************************************/
215 static void
infoBackupSaveCallback(void * data,const String * sectionNext,InfoSave * infoSaveData)216 infoBackupSaveCallback(void *data, const String *sectionNext, InfoSave *infoSaveData)
217 {
218     FUNCTION_TEST_BEGIN();
219         FUNCTION_TEST_PARAM_P(VOID, data);
220         FUNCTION_TEST_PARAM(STRING, sectionNext);
221         FUNCTION_TEST_PARAM(INFO_SAVE, infoSaveData);
222     FUNCTION_TEST_END();
223 
224     ASSERT(data != NULL);
225     ASSERT(infoSaveData != NULL);
226 
227     InfoBackup *infoBackup = (InfoBackup *)data;
228 
229     if (infoSaveSection(infoSaveData, INFO_BACKUP_SECTION_BACKUP_CURRENT_STR, sectionNext))
230     {
231         // Set the backup current section
232         for (unsigned int backupIdx = 0; backupIdx < infoBackupDataTotal(infoBackup); backupIdx++)
233         {
234             InfoBackupData backupData = infoBackupData(infoBackup, backupIdx);
235 
236             KeyValue *backupDataKv = kvNew();
237             kvPut(backupDataKv, VARSTR(INFO_KEY_FORMAT_STR), VARUINT(backupData.backrestFormat));
238             kvPut(backupDataKv, VARSTR(INFO_KEY_VERSION_STR), VARSTR(backupData.backrestVersion));
239 
240             kvPut(backupDataKv, INFO_KEY_DB_ID_VAR, VARUINT(backupData.backupPgId));
241 
242             kvPut(backupDataKv, INFO_BACKUP_KEY_BACKUP_ARCHIVE_START_VAR, VARSTR(backupData.backupArchiveStart));
243             kvPut(backupDataKv, INFO_BACKUP_KEY_BACKUP_ARCHIVE_STOP_VAR, VARSTR(backupData.backupArchiveStop));
244 
245             if (backupData.backupPrior != NULL)
246                 kvPut(backupDataKv, INFO_BACKUP_KEY_BACKUP_PRIOR_VAR, VARSTR(backupData.backupPrior));
247 
248             if (backupData.backupReference != NULL)
249             {
250                 kvPut(
251                     backupDataKv, INFO_BACKUP_KEY_BACKUP_REFERENCE_VAR, varNewVarLst(varLstNewStrLst(backupData.backupReference)));
252             }
253 
254             kvPut(backupDataKv, INFO_BACKUP_KEY_BACKUP_INFO_REPO_SIZE_VAR, VARUINT64(backupData.backupInfoRepoSize));
255             kvPut(backupDataKv, INFO_BACKUP_KEY_BACKUP_INFO_REPO_SIZE_DELTA_VAR, VARUINT64(backupData.backupInfoRepoSizeDelta));
256             kvPut(backupDataKv, INFO_BACKUP_KEY_BACKUP_INFO_SIZE_VAR, VARUINT64(backupData.backupInfoSize));
257             kvPut(backupDataKv, INFO_BACKUP_KEY_BACKUP_INFO_SIZE_DELTA_VAR, VARUINT64(backupData.backupInfoSizeDelta));
258 
259             // When storing time_t treat as signed int to avoid casting
260             kvPut(backupDataKv, INFO_BACKUP_KEY_BACKUP_TIMESTAMP_START_VAR, VARINT64(backupData.backupTimestampStart));
261             kvPut(backupDataKv, INFO_BACKUP_KEY_BACKUP_TIMESTAMP_STOP_VAR, VARINT64(backupData.backupTimestampStop));
262             kvPut(backupDataKv, INFO_BACKUP_KEY_BACKUP_TYPE_VAR, VARSTR(strIdToStr(backupData.backupType)));
263 
264             kvPut(backupDataKv, INFO_BACKUP_KEY_OPT_ARCHIVE_CHECK_VAR, VARBOOL(backupData.optionArchiveCheck));
265             kvPut(backupDataKv, INFO_BACKUP_KEY_OPT_ARCHIVE_COPY_VAR, VARBOOL(backupData.optionArchiveCopy));
266             kvPut(backupDataKv, INFO_BACKUP_KEY_OPT_BACKUP_STANDBY_VAR, VARBOOL(backupData.optionBackupStandby));
267             kvPut(backupDataKv, INFO_BACKUP_KEY_OPT_CHECKSUM_PAGE_VAR, VARBOOL(backupData.optionChecksumPage));
268             kvPut(backupDataKv, INFO_BACKUP_KEY_OPT_COMPRESS_VAR, VARBOOL(backupData.optionCompress));
269             kvPut(backupDataKv, INFO_BACKUP_KEY_OPT_HARDLINK_VAR, VARBOOL(backupData.optionHardlink));
270             kvPut(backupDataKv, INFO_BACKUP_KEY_OPT_ONLINE_VAR, VARBOOL(backupData.optionOnline));
271 
272             infoSaveValue(
273                 infoSaveData, INFO_BACKUP_SECTION_BACKUP_CURRENT_STR, backupData.backupLabel, jsonFromKv(backupDataKv));
274         }
275     }
276 
277     FUNCTION_TEST_RETURN_VOID();
278 }
279 
280 static void
infoBackupSave(InfoBackup * this,IoWrite * write)281 infoBackupSave(InfoBackup *this, IoWrite *write)
282 {
283     FUNCTION_LOG_BEGIN(logLevelDebug);
284         FUNCTION_LOG_PARAM(INFO_BACKUP, this);
285         FUNCTION_LOG_PARAM(IO_WRITE, write);
286     FUNCTION_LOG_END();
287 
288     ASSERT(this != NULL);
289     ASSERT(write != NULL);
290 
291     MEM_CONTEXT_TEMP_BEGIN()
292     {
293         infoPgSave(infoBackupPg(this), write, infoBackupSaveCallback, this);
294     }
295     MEM_CONTEXT_TEMP_END();
296 
297     FUNCTION_LOG_RETURN_VOID();
298 }
299 
300 /**********************************************************************************************************************************/
301 InfoBackup *
infoBackupPgSet(InfoBackup * this,unsigned int pgVersion,uint64_t pgSystemId,unsigned int pgCatalogVersion)302 infoBackupPgSet(InfoBackup *this, unsigned int pgVersion, uint64_t pgSystemId, unsigned int pgCatalogVersion)
303 {
304     FUNCTION_LOG_BEGIN(logLevelDebug);
305         FUNCTION_LOG_PARAM(INFO_BACKUP, this);
306         FUNCTION_LOG_PARAM(UINT, pgVersion);
307         FUNCTION_LOG_PARAM(UINT64, pgSystemId);
308         FUNCTION_LOG_PARAM(UINT, pgCatalogVersion);
309     FUNCTION_LOG_END();
310 
311     this->pub.infoPg = infoPgSet(infoBackupPg(this), infoPgBackup, pgVersion, pgSystemId, pgCatalogVersion);
312 
313     FUNCTION_LOG_RETURN(INFO_BACKUP, this);
314 }
315 
316 /**********************************************************************************************************************************/
317 InfoBackupData
infoBackupData(const InfoBackup * this,unsigned int backupDataIdx)318 infoBackupData(const InfoBackup *this, unsigned int backupDataIdx)
319 {
320     FUNCTION_LOG_BEGIN(logLevelTrace);
321         FUNCTION_LOG_PARAM(INFO_BACKUP, this);
322         FUNCTION_LOG_PARAM(UINT, backupDataIdx);
323     FUNCTION_LOG_END();
324 
325     ASSERT(this != NULL);
326 
327     FUNCTION_LOG_RETURN(INFO_BACKUP_DATA, *(InfoBackupData *)lstGet(this->pub.backup, backupDataIdx));
328 }
329 
330 /**********************************************************************************************************************************/
331 void
infoBackupDataAdd(const InfoBackup * this,const Manifest * manifest)332 infoBackupDataAdd(const InfoBackup *this, const Manifest *manifest)
333 {
334     FUNCTION_LOG_BEGIN(logLevelTrace);
335         FUNCTION_LOG_PARAM(INFO_BACKUP, this);
336         FUNCTION_LOG_PARAM(MANIFEST, manifest);
337     FUNCTION_LOG_END();
338 
339     ASSERT(this != NULL);
340     ASSERT(manifest != NULL);
341 
342     MEM_CONTEXT_TEMP_BEGIN()
343     {
344         const ManifestData *manData = manifestData(manifest);
345 
346         // Calculate backup sizes and references
347         uint64_t backupSize = 0;
348         uint64_t backupSizeDelta = 0;
349         uint64_t backupRepoSize = 0;
350         uint64_t backupRepoSizeDelta = 0;
351         StringList *referenceList = strLstNew();
352 
353         for (unsigned int fileIdx = 0; fileIdx < manifestFileTotal(manifest); fileIdx++)
354         {
355             const ManifestFile *file = manifestFile(manifest, fileIdx);
356 
357             backupSize += file->size;
358             backupRepoSize += file->sizeRepo > 0 ? file->sizeRepo : file->size;
359 
360             // If a reference to a file exists, then it is in a previous backup and the delta calculation was already done
361             if (file->reference != NULL)
362                 strLstAddIfMissing(referenceList, file->reference);
363             else
364             {
365                 backupSizeDelta += file->size;
366                 backupRepoSizeDelta += file->sizeRepo > 0 ? file->sizeRepo : file->size;
367             }
368         }
369 
370         MEM_CONTEXT_BEGIN(lstMemContext(this->pub.backup))
371         {
372             InfoBackupData infoBackupData =
373             {
374                 .backupLabel = strDup(manData->backupLabel),
375                 .backrestFormat = REPOSITORY_FORMAT,
376                 .backrestVersion = strDup(manData->backrestVersion),
377                 .backupInfoRepoSize = backupRepoSize,
378                 .backupInfoRepoSizeDelta = backupRepoSizeDelta,
379                 .backupInfoSize = backupSize,
380                 .backupInfoSizeDelta = backupSizeDelta,
381                 .backupPgId = manData->pgId,
382                 .backupTimestampStart = manData->backupTimestampStart,
383                 .backupTimestampStop= manData->backupTimestampStop,
384                 .backupType = manData->backupType,
385 
386                 .backupArchiveStart = strDup(manData->archiveStart),
387                 .backupArchiveStop = strDup(manData->archiveStop),
388 
389                 .optionArchiveCheck = manData->backupOptionArchiveCheck,
390                 .optionArchiveCopy = manData->backupOptionArchiveCopy,
391                 .optionBackupStandby = manData->backupOptionStandby != NULL ? varBool(manData->backupOptionStandby) : false,
392                 .optionChecksumPage = manData->backupOptionChecksumPage != NULL ?
393                     varBool(manData->backupOptionChecksumPage) : false,
394                 .optionCompress = manData->backupOptionCompressType != compressTypeNone,
395                 .optionHardlink = manData->backupOptionHardLink,
396                 .optionOnline = manData->backupOptionOnline,
397             };
398 
399             if (manData->backupType != backupTypeFull)
400             {
401                 strLstSort(referenceList, sortOrderAsc);
402                 infoBackupData.backupReference = strLstDup(referenceList);
403                 infoBackupData.backupPrior = strDup(manData->backupLabelPrior);
404             }
405 
406             // Add the backup data to the current backup list
407             lstAdd(this->pub.backup, &infoBackupData);
408 
409             // Ensure the list is sorted ascending by the backupLabel
410             lstSort(this->pub.backup, sortOrderAsc);
411         }
412         MEM_CONTEXT_END();
413     }
414     MEM_CONTEXT_TEMP_END();
415 
416     FUNCTION_LOG_RETURN_VOID();
417 }
418 
419 /**********************************************************************************************************************************/
420 void
infoBackupDataDelete(const InfoBackup * this,const String * backupDeleteLabel)421 infoBackupDataDelete(const InfoBackup *this, const String *backupDeleteLabel)
422 {
423     FUNCTION_LOG_BEGIN(logLevelTrace);
424         FUNCTION_LOG_PARAM(INFO_BACKUP, this);
425         FUNCTION_LOG_PARAM(STRING, backupDeleteLabel);
426     FUNCTION_LOG_END();
427 
428     ASSERT(this != NULL);
429 
430     for (unsigned int idx = 0; idx < infoBackupDataTotal(this); idx++)
431     {
432         InfoBackupData backupData = infoBackupData(this, idx);
433 
434         if (strCmp(backupData.backupLabel, backupDeleteLabel) == 0)
435             lstRemoveIdx(this->pub.backup, idx);
436     }
437 
438     FUNCTION_LOG_RETURN_VOID();
439 }
440 
441 /**********************************************************************************************************************************/
442 StringList *
infoBackupDataLabelList(const InfoBackup * this,const String * expression)443 infoBackupDataLabelList(const InfoBackup *this, const String *expression)
444 {
445     FUNCTION_LOG_BEGIN(logLevelTrace);
446         FUNCTION_LOG_PARAM(INFO_BACKUP, this);
447         FUNCTION_LOG_PARAM(STRING, expression);
448     FUNCTION_LOG_END();
449 
450     ASSERT(this != NULL);
451 
452     // Return a 0 sized list if no current backups or none matching the filter
453     StringList *result = strLstNew();
454 
455     MEM_CONTEXT_TEMP_BEGIN()
456     {
457         // Prepare regexp if an expression was passed
458         RegExp *regExp = (expression == NULL) ? NULL : regExpNew(expression);
459 
460         // For each backup label, compare it to the filter (if any) and sort it for return
461         for (unsigned int backupLabelIdx = 0; backupLabelIdx < infoBackupDataTotal(this); backupLabelIdx++)
462         {
463             InfoBackupData backupData = infoBackupData(this, backupLabelIdx);
464 
465             if (regExp == NULL || regExpMatch(regExp, backupData.backupLabel))
466             {
467                 strLstAdd(result, backupData.backupLabel);
468             }
469         }
470     }
471     MEM_CONTEXT_TEMP_END();
472 
473     FUNCTION_LOG_RETURN(STRING_LIST, result);
474 }
475 
476 /**********************************************************************************************************************************/
477 StringList *
infoBackupDataDependentList(const InfoBackup * this,const String * backupLabel)478 infoBackupDataDependentList(const InfoBackup *this, const String *backupLabel)
479 {
480     FUNCTION_LOG_BEGIN(logLevelTrace);
481         FUNCTION_LOG_PARAM(INFO_BACKUP, this);
482         FUNCTION_LOG_PARAM(STRING, backupLabel);
483     FUNCTION_LOG_END();
484 
485     ASSERT(this != NULL);
486     ASSERT(backupLabel != NULL);
487 
488     // Return the given label as the only dependency or the given label and a list of labels that depend on it
489     StringList *result = strLstNew();
490     strLstAdd(result, backupLabel);
491 
492     MEM_CONTEXT_TEMP_BEGIN()
493     {
494         // For each backup label from oldest to newest in the current section, add each dependency to the list
495         for (unsigned int backupLabelIdx = 0; backupLabelIdx < infoBackupDataTotal(this); backupLabelIdx++)
496         {
497             InfoBackupData backupData = infoBackupData(this, backupLabelIdx);
498 
499             // If the backupPrior is in the dependency chain add the label to the list
500             if (backupData.backupPrior != NULL && strLstExists(result, backupData.backupPrior))
501                 strLstAdd(result, backupData.backupLabel);
502         }
503     }
504     MEM_CONTEXT_TEMP_END();
505 
506     FUNCTION_LOG_RETURN(STRING_LIST, result);
507 }
508 
509 /**********************************************************************************************************************************/
510 typedef struct InfoBackupLoadFileData
511 {
512     MemContext *memContext;                                         // Mem context
513     const Storage *storage;                                         // Storage to load from
514     const String *fileName;                                         // Base filename
515     CipherType cipherType;                                          // Cipher type
516     const String *cipherPass;                                       // Cipher passphrase
517     InfoBackup *infoBackup;                                         // Loaded infoBackup object
518 } InfoBackupLoadFileData;
519 
520 static bool
infoBackupLoadFileCallback(void * data,unsigned int try)521 infoBackupLoadFileCallback(void *data, unsigned int try)
522 {
523     FUNCTION_LOG_BEGIN(logLevelTrace);
524         FUNCTION_LOG_PARAM_P(VOID, data);
525         FUNCTION_LOG_PARAM(UINT, try);
526     FUNCTION_LOG_END();
527 
528     ASSERT(data != NULL);
529 
530     InfoBackupLoadFileData *loadData = (InfoBackupLoadFileData *)data;
531     bool result = false;
532 
533     if (try < 2)
534     {
535         // Construct filename based on try
536         const String *fileName = try == 0 ? loadData->fileName : strNewFmt("%s" INFO_COPY_EXT, strZ(loadData->fileName));
537 
538         // Attempt to load the file
539         IoRead *read = storageReadIo(storageNewReadP(loadData->storage, fileName));
540         cipherBlockFilterGroupAdd(ioReadFilterGroup(read), loadData->cipherType, cipherModeDecrypt, loadData->cipherPass);
541 
542         MEM_CONTEXT_BEGIN(loadData->memContext)
543         {
544             loadData->infoBackup = infoBackupNewLoad(read);
545             result = true;
546         }
547         MEM_CONTEXT_END();
548     }
549 
550     FUNCTION_LOG_RETURN(BOOL, result);
551 }
552 
553 InfoBackup *
infoBackupLoadFile(const Storage * storage,const String * fileName,CipherType cipherType,const String * cipherPass)554 infoBackupLoadFile(const Storage *storage, const String *fileName, CipherType cipherType, const String *cipherPass)
555 {
556     FUNCTION_LOG_BEGIN(logLevelDebug);
557         FUNCTION_LOG_PARAM(STORAGE, storage);
558         FUNCTION_LOG_PARAM(STRING, fileName);
559         FUNCTION_LOG_PARAM(STRING_ID, cipherType);
560         FUNCTION_TEST_PARAM(STRING, cipherPass);
561     FUNCTION_LOG_END();
562 
563     ASSERT(storage != NULL);
564     ASSERT(fileName != NULL);
565     ASSERT((cipherType == cipherTypeNone && cipherPass == NULL) || (cipherType != cipherTypeNone && cipherPass != NULL));
566 
567     InfoBackupLoadFileData data =
568     {
569         .memContext = memContextCurrent(),
570         .storage = storage,
571         .fileName = fileName,
572         .cipherType = cipherType,
573         .cipherPass = cipherPass,
574     };
575 
576     MEM_CONTEXT_TEMP_BEGIN()
577     {
578         const char *fileNamePath = strZ(storagePathP(storage, fileName));
579 
580         TRY_BEGIN()
581         {
582             infoLoad(
583                 strNewFmt("unable to load info file '%s' or '%s" INFO_COPY_EXT "'", fileNamePath, fileNamePath),
584                 infoBackupLoadFileCallback, &data);
585         }
586         CATCH_ANY()
587         {
588             THROWP_FMT(
589                 errorType(),
590                 "%s\n"
591                 "HINT: backup.info cannot be opened and is required to perform a backup.\n"
592                 "HINT: has a stanza-create been performed?",
593                 errorMessage());
594         }
595         TRY_END();
596     }
597     MEM_CONTEXT_TEMP_END();
598 
599     FUNCTION_LOG_RETURN(INFO_BACKUP, data.infoBackup);
600 }
601 
602 /**********************************************************************************************************************************/
603 InfoBackup *
infoBackupLoadFileReconstruct(const Storage * storage,const String * fileName,CipherType cipherType,const String * cipherPass)604 infoBackupLoadFileReconstruct(const Storage *storage, const String *fileName, CipherType cipherType, const String *cipherPass)
605 {
606     FUNCTION_LOG_BEGIN(logLevelDebug);
607         FUNCTION_LOG_PARAM(STORAGE, storage);
608         FUNCTION_LOG_PARAM(STRING, fileName);
609         FUNCTION_LOG_PARAM(STRING_ID, cipherType);
610         FUNCTION_TEST_PARAM(STRING, cipherPass);
611     FUNCTION_LOG_END();
612 
613     ASSERT(storage != NULL);
614     ASSERT(fileName != NULL);
615     ASSERT((cipherType == cipherTypeNone && cipherPass == NULL) || (cipherType != cipherTypeNone && cipherPass != NULL));
616 
617     InfoBackup *infoBackup = infoBackupLoadFile(storage, fileName, cipherType, cipherPass);
618 
619     MEM_CONTEXT_TEMP_BEGIN()
620     {
621         // Get a list of backups in the repo
622         StringList *backupList = strLstSort(
623             storageListP(
624                 storage, STORAGE_REPO_BACKUP_STR,
625                 .expression = backupRegExpP(.full = true, .differential = true, .incremental = true)),
626             sortOrderAsc);
627 
628         // Get the list of current backups and remove backups from current that are no longer in the repository
629         StringList *backupCurrentList = strLstSort(infoBackupDataLabelList(infoBackup, NULL), sortOrderAsc);
630 
631         for (unsigned int backupCurrIdx = 0; backupCurrIdx < strLstSize(backupCurrentList); backupCurrIdx++)
632         {
633             String *backupLabel = strLstGet(backupCurrentList, backupCurrIdx);
634             String *manifestFileName = strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strZ(backupLabel));
635 
636             // If the manifest does not exist on disk and this backup has not already been deleted from the current list in the
637             // infoBackup object, then remove it and its dependencies
638             if (!storageExistsP(storage, manifestFileName) && infoBackupDataByLabel(infoBackup, backupLabel) != NULL)
639             {
640                 StringList *backupList = strLstSort(infoBackupDataDependentList(infoBackup, backupLabel), sortOrderDesc);
641 
642                 for (unsigned int backupIdx = 0; backupIdx < strLstSize(backupList); backupIdx++)
643                 {
644                     String *removeBackup = strLstGet(backupList, backupIdx);
645 
646                     LOG_WARN_FMT("backup '%s' missing manifest removed from " INFO_BACKUP_FILE, strZ(removeBackup));
647 
648                     infoBackupDataDelete(infoBackup, removeBackup);
649                 }
650             }
651         }
652 
653         // Get the updated current list of backups from backup.info
654         backupCurrentList = strLstSort(infoBackupDataLabelList(infoBackup, NULL), sortOrderAsc);
655 
656         // For each backup in the repo, check if it exists in backup.info
657         for (unsigned int backupIdx = 0; backupIdx < strLstSize(backupList); backupIdx++)
658         {
659             String *backupLabel = strLstGet(backupList, backupIdx);
660 
661             // If it does not exist in the list of current backups, then if it is valid, add it
662             if (!strLstExists(backupCurrentList, backupLabel))
663             {
664                 String *manifestFileName = strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strZ(backupLabel));
665 
666                 // Check if a completed backup exists (backup.manifest only - ignore .copy)
667                 if (storageExistsP(storage, manifestFileName))
668                 {
669                     bool found = false;
670                     const Manifest *manifest = manifestLoadFile(
671                         storage, manifestFileName, cipherType, infoPgCipherPass(infoBackupPg(infoBackup)));
672                     const ManifestData *manData = manifestData(manifest);
673 
674                     // If the pg data for the manifest exists in the history, then add it to current, but if something doesn't match
675                     // then warn that the backup is not valid
676                     for (unsigned int pgIdx = 0; pgIdx < infoPgDataTotal(infoBackupPg(infoBackup)); pgIdx++)
677                     {
678                         InfoPgData pgHistory = infoPgData(infoBackupPg(infoBackup), pgIdx);
679 
680                         // If there is an exact match with the history, system and version and there is no backup-prior dependency
681                         // or there is a backup-prior and it is in the list, then add this backup to the current backup list
682                         if (manData->pgId == pgHistory.id && manData->pgSystemId == pgHistory.systemId &&
683                             manData->pgVersion == pgHistory.version &&
684                             (manData->backupLabelPrior == NULL ||
685                                 infoBackupDataByLabel(infoBackup, manData->backupLabelPrior) != NULL))
686                         {
687                             LOG_WARN_FMT("backup '%s' found in repository added to " INFO_BACKUP_FILE, strZ(backupLabel));
688                             infoBackupDataAdd(infoBackup, manifest);
689                             found = true;
690                             break;
691                         }
692                     }
693 
694                     if (!found)
695                         LOG_WARN_FMT("invalid backup '%s' cannot be added to current backups", strZ(manData->backupLabel));
696                 }
697             }
698         }
699     }
700     MEM_CONTEXT_TEMP_END();
701 
702     FUNCTION_LOG_RETURN(INFO_BACKUP, infoBackup);
703 }
704 
705 /**********************************************************************************************************************************/
706 void
infoBackupSaveFile(InfoBackup * infoBackup,const Storage * storage,const String * fileName,CipherType cipherType,const String * cipherPass)707 infoBackupSaveFile(
708     InfoBackup *infoBackup, const Storage *storage, const String *fileName, CipherType cipherType, const String *cipherPass)
709 {
710     FUNCTION_LOG_BEGIN(logLevelDebug);
711         FUNCTION_LOG_PARAM(INFO_BACKUP, infoBackup);
712         FUNCTION_LOG_PARAM(STORAGE, storage);
713         FUNCTION_LOG_PARAM(STRING, fileName);
714         FUNCTION_LOG_PARAM(STRING_ID, cipherType);
715         FUNCTION_TEST_PARAM(STRING, cipherPass);
716     FUNCTION_LOG_END();
717 
718     ASSERT(infoBackup != NULL);
719     ASSERT(storage != NULL);
720     ASSERT(fileName != NULL);
721     ASSERT((cipherType == cipherTypeNone && cipherPass == NULL) || (cipherType != cipherTypeNone && cipherPass != NULL));
722 
723     MEM_CONTEXT_TEMP_BEGIN()
724     {
725         // Write output into a buffer since it needs to be saved to storage twice
726         Buffer *buffer = bufNew(ioBufferSize());
727         IoWrite *write = ioBufferWriteNew(buffer);
728         cipherBlockFilterGroupAdd(ioWriteFilterGroup(write), cipherType, cipherModeEncrypt, cipherPass);
729         infoBackupSave(infoBackup, write);
730 
731         // Save the file and make a copy
732         storagePutP(storageNewWriteP(storage, fileName), buffer);
733         storagePutP(storageNewWriteP(storage, strNewFmt("%s" INFO_COPY_EXT, strZ(fileName))), buffer);
734     }
735     MEM_CONTEXT_TEMP_END();
736 
737     FUNCTION_LOG_RETURN_VOID();
738 }
739 
740 /**********************************************************************************************************************************/
741 String *
infoBackupDataToLog(const InfoBackupData * this)742 infoBackupDataToLog(const InfoBackupData *this)
743 {
744     return strNewFmt("{label: %s, pgId: %u}", strZ(this->backupLabel), this->backupPgId);
745 }
746