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