1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-01-18 04:58:51 +02:00

Migrate backup manifest load/save to C.

The backup manifest stores a complete list of all files, links, and paths in a backup along with metadata such as checksums, sizes,
timestamps, etc.  A list of databases is also included for selective restore.

The purpose of the manifest is to allow the restore command to confidently reconstruct the PostgreSQL data directory and ensure that
nothing is missing or corrupt.  It is also useful for reporting, e.g. size of backup, backup time, etc.

For now, migrate enough functionality to implement the restore command.

Reviewed by Cynthia Shang.
This commit is contained in:
David Steele 2019-09-23 13:50:46 -04:00
parent 5b64c93e8b
commit c969137021
6 changed files with 2820 additions and 4 deletions

View File

@ -15,6 +15,14 @@
<release date="XXXX-XX-XX" version="2.18dev" title="UNDER DEVELOPMENT">
<release-core-list>
<release-development-list>
<release-item>
<release-item-contributor-list>
<release-item-reviewer id="cynthia.shang"/>
</release-item-contributor-list>
<p>Migrate backup manifest load/save to C.</p>
</release-item>
<release-item>
<release-item-contributor-list>
<release-item-reviewer id="cynthia.shang"/>

View File

@ -136,6 +136,7 @@ SRCS = \
info/info.c \
info/infoArchive.c \
info/infoBackup.c \
info/manifest.c \
info/infoPg.c \
perl/config.c \
perl/exec.c \
@ -261,7 +262,7 @@ command/control/start.o: command/control/start.c build.auto.h command/control/co
command/control/stop.o: command/control/stop.c build.auto.h command/control/common.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/read.intern.h common/io/write.h common/io/write.intern.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h storage/helper.h storage/info.h storage/read.h storage/read.intern.h storage/storage.h storage/storage.intern.h storage/write.h storage/write.intern.h version.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/control/stop.c -o command/control/stop.o
command/expire/expire.o: command/expire/expire.c build.auto.h command/archive/common.h command/backup/common.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/regExp.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
command/expire/expire.o: command/expire/expire.c build.auto.h command/archive/common.h command/backup/common.h common/assert.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/regExp.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/expire/expire.c -o command/expire/expire.o
command/help/help.o: command/help/help.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/handleWrite.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h version.h
@ -288,7 +289,7 @@ command/stanza/common.o: command/stanza/common.c build.auto.h command/check/comm
command/stanza/create.o: command/stanza/create.c build.auto.h command/control/common.h command/stanza/common.h command/stanza/create.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h postgres/interface.h postgres/version.h protocol/client.h protocol/command.h protocol/helper.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/stanza/create.c -o command/stanza/create.o
command/stanza/delete.o: command/stanza/delete.c build.auto.h command/backup/common.h command/control/common.h command/stanza/delete.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h postgres/interface.h protocol/client.h protocol/command.h protocol/helper.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
command/stanza/delete.o: command/stanza/delete.c build.auto.h command/backup/common.h command/control/common.h command/stanza/delete.h common/assert.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h postgres/interface.h protocol/client.h protocol/command.h protocol/helper.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/stanza/delete.c -o command/stanza/delete.o
command/stanza/upgrade.o: command/stanza/upgrade.c build.auto.h command/control/common.h command/stanza/common.h command/stanza/upgrade.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h postgres/interface.h postgres/version.h protocol/client.h protocol/command.h protocol/helper.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
@ -483,12 +484,15 @@ info/info.o: info/info.c build.auto.h common/assert.h common/crypto/hash.h commo
info/infoArchive.o: info/infoArchive.c build.auto.h common/assert.h common/crypto/cipherBlock.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/log.h common/logLevel.h common/macro.h common/memContext.h common/object.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h info/info.h info/infoArchive.h info/infoPg.h postgres/interface.h postgres/version.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c info/infoArchive.c -o info/infoArchive.o
info/infoBackup.o: info/infoBackup.c build.auto.h common/assert.h common/crypto/cipherBlock.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/log.h common/logLevel.h common/macro.h common/memContext.h common/object.h common/regExp.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h info/info.h info/infoBackup.h info/infoPg.h info/manifest.h postgres/interface.h postgres/version.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
info/infoBackup.o: info/infoBackup.c build.auto.h command/backup/common.h common/assert.h common/crypto/cipherBlock.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/log.h common/logLevel.h common/macro.h common/memContext.h common/object.h common/regExp.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h info/info.h info/infoBackup.h info/infoPg.h info/manifest.h postgres/interface.h postgres/version.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c info/infoBackup.c -o info/infoBackup.o
info/infoPg.o: info/infoPg.c build.auto.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/log.h common/logLevel.h common/macro.h common/memContext.h common/object.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h info/info.h info/infoPg.h postgres/interface.h postgres/version.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c info/infoPg.c -o info/infoPg.o
info/manifest.o: info/manifest.c build.auto.h command/backup/common.h common/assert.h common/crypto/cipherBlock.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/list.h common/type/mcv.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h info/info.h info/manifest.h postgres/interface.h postgres/version.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c info/manifest.c -o info/manifest.o
main.o: main.c build.auto.h command/archive/get/get.h command/archive/push/push.h command/check/check.h command/command.h command/control/start.h command/control/stop.h command/expire/expire.h command/help/help.h command/info/info.h command/local/local.h command/remote/remote.h command/stanza/create.h command/stanza/delete.h command/stanza/upgrade.h command/storage/list.h common/assert.h common/debug.h common/error.auto.h common/error.h common/exit.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h config/load.h perl/exec.h postgres/interface.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h version.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c main.c -o main.o

2004
src/info/manifest.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,246 @@
/***********************************************************************************************************************************
Manifest Handler
Backup Manifest Handler
The backup manifest stores a complete list of all files, links, and paths in a backup along with metadata such as checksums, sizes,
timestamps, etc. A list of databases is also included for selective restore.
The purpose of the manifest is to allow the restore command to confidently reconstruct the PostgreSQL data directory and ensure that
nothing is missing or corrupt. It is also useful for reporting, e.g. size of backup, backup time, etc.
***********************************************************************************************************************************/
#ifndef INFO_MANIFEST_H
#define INFO_MANIFEST_H
#include "command/backup/common.h"
#include "common/crypto/common.h"
#include "common/type/variantList.h"
/***********************************************************************************************************************************
Constants
***********************************************************************************************************************************/
#define BACKUP_MANIFEST_FILE "backup.manifest"
STRING_DECLARE(BACKUP_MANIFEST_FILE_STR);
#define MANIFEST_TARGET_PGDATA "pg_data"
STRING_DECLARE(MANIFEST_TARGET_PGDATA_STR);
#define MANIFEST_TARGET_PGTBLSPC "pg_tblspc"
STRING_DECLARE(MANIFEST_TARGET_PGTBLSPC_STR);
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
typedef struct Manifest Manifest;
#include "common/crypto/hash.h"
#include "storage/storage.h"
/***********************************************************************************************************************************
Manifest data
***********************************************************************************************************************************/
typedef struct ManifestData
{
const String *backupLabel; // Backup label (unique identifier for the backup)
const String *backupLabelPrior; // Backup label for backup this diff/incr is based on
time_t backupTimestampCopyStart; // When did the file copy start?
time_t backupTimestampStart; // When did the backup start?
time_t backupTimestampStop; // When did the backup stop?
BackupType backupType; // Type of backup: full, diff, incr
// ??? Note that these fields are redundant and verbose since storing the start/stop lsn as a uint64 would be sufficient.
// However, we currently lack the functions to transform these values back and forth so this will do for now.
const String *archiveStart; // First WAL file in the backup
const String *archiveStop; // Last WAL file in the backup
const String *lsnStart; // Start LSN for the backup
const String *lsnStop; // Stop LSN for the backup
unsigned int pgId; // PostgreSQL id in backup.info
unsigned int pgVersion; // PostgreSQL version
uint64_t pgSystemId; // PostgreSQL system identifier
bool backupOptionArchiveCheck; // Will WAL segments be checked at the end of the backup?
bool backupOptionArchiveCopy; // Will WAL segments be copied to the backup?
const Variant *backupOptionStandby; // Will the backup be performed from a standby?
const Variant *backupOptionBufferSize; // Buffer size used for file/protocol operations
const Variant *backupOptionChecksumPage; // Will page checksums be verified?
bool backupOptionCompress; // Will compression be used for backup?
const Variant *backupOptionCompressLevel; // Level to use for compression
const Variant *backupOptionCompressLevelNetwork; // Level to use for network compression
const Variant *backupOptionDelta; // Will a checksum delta be performed?
bool backupOptionHardLink; // Will hardlinks be created in the backup?
bool backupOptionOnline; // Will an online backup be performed?
const Variant *backupOptionProcessMax; // How many processes will be used for backup?
} ManifestData;
/***********************************************************************************************************************************
Db type
***********************************************************************************************************************************/
typedef struct ManifestDb
{
const String *name; // Db name (must be first member in struct)
unsigned int id; // Db oid
unsigned int lastSystemId; // Highest oid used by system objects in this database
} ManifestDb;
/***********************************************************************************************************************************
File type
***********************************************************************************************************************************/
typedef struct ManifestFile
{
const String *name; // File name (must be first member in struct)
bool primary:1; // Should this file be copied from the primary?
bool checksumPage:1; // Does this file have page checksums?
bool checksumPageError:1; // Is there an error in the page checksum?
mode_t mode; // File mode
char checksumSha1[HASH_TYPE_SHA1_SIZE_HEX + 1]; // SHA1 checksum
const VariantList *checksumPageErrorList; // List of page checksum errors if there are any
const String *user; // User name
const String *group; // Group name
const String *reference; // Reference to a prior backup
uint64_t size; // Original size
uint64_t sizeRepo; // Size in repo
time_t timestamp; // Original timestamp
} ManifestFile;
/***********************************************************************************************************************************
Link type
***********************************************************************************************************************************/
typedef struct ManifestLink
{
const String *name; // Link name (must be first member in struct)
const String *destination; // Link destination
const String *user; // User name
const String *group; // Group name
} ManifestLink;
/***********************************************************************************************************************************
Path type
***********************************************************************************************************************************/
typedef struct ManifestPath
{
const String *name; // Path name (must be first member in struct)
mode_t mode; // Directory mode
const String *user; // User name
const String *group; // Group name
} ManifestPath;
/***********************************************************************************************************************************
Target type
***********************************************************************************************************************************/
typedef enum
{
manifestTargetTypePath,
manifestTargetTypeLink,
} ManifestTargetType;
typedef struct ManifestTarget
{
const String *name; // Target name (must be first member in struct)
ManifestTargetType type; // Target type
const String *path; // Target path (if path or link)
const String *file; // Target file (if file link)
unsigned int tablespaceId; // Oid if this link is a tablespace
const String *tablespaceName; // Name of the tablespace
} ManifestTarget;
/***********************************************************************************************************************************
Constructor
***********************************************************************************************************************************/
Manifest *manifestNewLoad(IoRead *read);
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
void manifestLinkCheck(const Manifest *this);
void manifestSave(Manifest *this, IoWrite *write);
/***********************************************************************************************************************************
Db functions and getters/setters
***********************************************************************************************************************************/
const ManifestDb *manifestDb(const Manifest *this, unsigned int dbIdx);
const ManifestDb *manifestDbFind(const Manifest *this, const String *name);
const ManifestDb *manifestDbFindDefault(const Manifest *this, const String *name, const ManifestDb *dbDefault);
unsigned int manifestDbTotal(const Manifest *this);
/***********************************************************************************************************************************
File functions and getters/setters
***********************************************************************************************************************************/
const ManifestFile *manifestFile(const Manifest *this, unsigned int fileIdx);
const ManifestFile *manifestFileFind(const Manifest *this, const String *name);
const ManifestFile *manifestFileFindDefault(const Manifest *this, const String *name, const ManifestFile *fileDefault);
void manifestFileRemove(const Manifest *this, const String *name);
unsigned int manifestFileTotal(const Manifest *this);
/***********************************************************************************************************************************
Link functions and getters/setters
***********************************************************************************************************************************/
const ManifestLink *manifestLink(const Manifest *this, unsigned int linkIdx);
const ManifestLink *manifestLinkFind(const Manifest *this, const String *name);
const ManifestLink *manifestLinkFindDefault(const Manifest *this, const String *name, const ManifestLink *linkDefault);
void manifestLinkRemove(const Manifest *this, const String *name);
unsigned int manifestLinkTotal(const Manifest *this);
void manifestLinkUpdate(const Manifest *this, const String *name, const String *path);
/***********************************************************************************************************************************
Path functions and getters/setters
***********************************************************************************************************************************/
const ManifestPath *manifestPath(const Manifest *this, unsigned int pathIdx);
const ManifestPath *manifestPathFind(const Manifest *this, const String *name);
const ManifestPath *manifestPathFindDefault(const Manifest *this, const String *name, const ManifestPath *pathDefault);
unsigned int manifestPathTotal(const Manifest *this);
/***********************************************************************************************************************************
Target functions and getters/setters
***********************************************************************************************************************************/
const ManifestTarget *manifestTarget(const Manifest *this, unsigned int targetIdx);
const ManifestTarget *manifestTargetFind(const Manifest *this, const String *name);
void manifestTargetRemove(const Manifest *this, const String *name);
unsigned int manifestTargetTotal(const Manifest *this);
void manifestTargetUpdate(const Manifest *this, const String *name, const String *path, const String *file);
/***********************************************************************************************************************************
Getters
***********************************************************************************************************************************/
const String *manifestCipherSubPass(const Manifest *this);
const ManifestData *manifestData(const Manifest *this);
String *manifestPgPath(const String *manifestPath);
const ManifestTarget *manifestTargetBase(const Manifest *this);
String *manifestTargetPath(const Manifest *this, const ManifestTarget *target);
/***********************************************************************************************************************************
Helper functions
***********************************************************************************************************************************/
Manifest *manifestLoadFile(const Storage *storage, const String *fileName, CipherType cipherType, const String *cipherPass);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
#define FUNCTION_LOG_MANIFEST_TYPE \
Manifest *
#define FUNCTION_LOG_MANIFEST_FORMAT(value, buffer, bufferSize) \
objToLog(value, "Manifest", buffer, bufferSize)
#define FUNCTION_LOG_MANIFEST_DB_TYPE \
ManifestDb *
#define FUNCTION_LOG_MANIFEST_DB_FORMAT(value, buffer, bufferSize) \
objToLog(value, "ManifestDb", buffer, bufferSize)
#define FUNCTION_LOG_MANIFEST_FILE_TYPE \
ManifestFile *
#define FUNCTION_LOG_MANIFEST_FILE_FORMAT(value, buffer, bufferSize) \
objToLog(value, "ManifestFile", buffer, bufferSize)
#define FUNCTION_LOG_MANIFEST_LINK_TYPE \
ManifestLink *
#define FUNCTION_LOG_MANIFEST_LINK_FORMAT(value, buffer, bufferSize) \
objToLog(value, "ManifestLink", buffer, bufferSize)
#define FUNCTION_LOG_MANIFEST_PATH_TYPE \
ManifestPath *
#define FUNCTION_LOG_MANIFEST_PATH_FORMAT(value, buffer, bufferSize) \
objToLog(value, "ManifestPath", buffer, bufferSize)
#define FUNCTION_LOG_MANIFEST_TARGET_TYPE \
ManifestTarget *
#define FUNCTION_LOG_MANIFEST_TARGET_FORMAT(value, buffer, bufferSize) \
objToLog(value, "ManifestTarget", buffer, bufferSize)
#endif

View File

@ -549,6 +549,13 @@ unit:
coverage:
info/infoBackup: full
# ----------------------------------------------------------------------------------------------------------------------------
- name: manifest
total: 4
coverage:
info/manifest: full
# ----------------------------------------------------------------------------------------------------------------------------
- name: info-backup-perl
total: 3

View File

@ -0,0 +1,559 @@
/***********************************************************************************************************************************
Test Backup Manifest Handler
***********************************************************************************************************************************/
#include "common/io/bufferRead.h"
#include "common/io/bufferWrite.h"
#include "info/infoBackup.h"
#include "storage/posix/storage.h"
#include "common/harnessInfo.h"
/***********************************************************************************************************************************
Test Run
***********************************************************************************************************************************/
void
testRun(void)
{
Storage *storageTest = storagePosixNew(
strNew(testPath()), STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, true, NULL);
// *****************************************************************************************************************************
if (testBegin("struct sizes"))
{
// Make sure the size of structs doesn't change without us knowing about it
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_UINT(sizeof(ManifestLoadFound), TEST_64BIT() ? 1 : 1, "check size of ManifestLoadFound");
TEST_RESULT_UINT(sizeof(ManifestPath), TEST_64BIT() ? 32 : 16, "check size of ManifestPath");
TEST_RESULT_UINT(sizeof(ManifestFile), TEST_64BIT() ? 120 : 92, "check size of ManifestFile");
}
// *****************************************************************************************************************************
if (testBegin("manifestNewLoad() and manifestSave()"))
{
Manifest *manifest = NULL;
// Manifest with minimal features
// -------------------------------------------------------------------------------------------------------------------------
const Buffer *contentLoad = harnessInfoChecksumZ
(
"[backup]\n"
"backup-label=\"20190808-163540F\"\n"
"backup-timestamp-copy-start=1565282141\n"
"backup-timestamp-start=1565282140\n"
"backup-timestamp-stop=1565282142\n"
"backup-type=\"full\"\n"
"\n"
"[backup:db]\n"
"db-catalog-version=201409291\n"
"db-control-version=942\n"
"db-id=1\n"
"db-system-id=1000000000000000094\n"
"db-version=\"9.4\"\n"
"\n"
"[backup:option]\n"
"option-archive-check=true\n"
"option-archive-copy=true\n"
"option-compress=false\n"
"option-hardlink=false\n"
"option-online=false\n"
"\n"
"[backup:target]\n"
"pg_data={\"path\":\"/pg/base\",\"type\":\"path\"}\n"
"\n"
"[cipher]\n"
"cipher-pass=\"somepass\"\n"
"\n"
"[target:file]\n"
"pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"size\":4,\"timestamp\":1565282114}\n"
"\n"
"[target:file:default]\n"
"group=\"group1\"\n"
"master=true\n"
"mode=\"0600\"\n"
"user=\"user1\"\n"
"\n"
"[target:path]\n"
"pg_data={}\n"
"\n"
"[target:path:default]\n"
"group=\"group1\"\n"
"mode=\"0700\"\n"
"user=\"user1\"\n"
);
TEST_ASSIGN(manifest, manifestNewLoad(ioBufferReadNew(contentLoad)), "load manifest");
TEST_ERROR(
manifestTargetFind(manifest, STRDEF("bogus")), AssertError, "unable to find 'bogus' in manifest target list");
TEST_RESULT_STR_Z(manifestData(manifest)->backupLabel, "20190808-163540F", " check manifest data");
TEST_RESULT_STR_Z(manifestCipherSubPass(manifest), "somepass", " check cipher subpass");
TEST_RESULT_VOID(
manifestTargetUpdate(manifest, MANIFEST_TARGET_PGDATA_STR, STRDEF("/pg/base"), NULL), " update target no change");
TEST_RESULT_VOID(
manifestTargetUpdate(manifest, MANIFEST_TARGET_PGDATA_STR, STRDEF("/path2"), NULL), " update target");
TEST_RESULT_STR_Z(
manifestTargetFind(manifest, MANIFEST_TARGET_PGDATA_STR)->path, "/path2", " check target path");
TEST_RESULT_VOID(
manifestTargetUpdate(manifest, MANIFEST_TARGET_PGDATA_STR, STRDEF("/pg/base"), NULL), " fix target path");
Buffer *contentSave = bufNew(0);
TEST_RESULT_VOID(manifestSave(manifest, ioBufferWriteNew(contentSave)), "save manifest");
TEST_RESULT_STR_STR(strNewBuf(contentSave), strNewBuf(contentLoad), " check save");
// Manifest with all features
// -------------------------------------------------------------------------------------------------------------------------
#define TEST_MANIFEST_HEADER \
"[backup]\n" \
"backup-archive-start=\"000000030000028500000089\"\n" \
"backup-archive-stop=\"000000030000028500000089\"\n" \
"backup-label=\"20190818-084502F_20190820-084502D\"\n" \
"backup-lsn-start=\"285/89000028\"\n" \
"backup-lsn-stop=\"285/89001F88\"\n" \
"backup-prior=\"20190818-084502F\"\n" \
"backup-timestamp-copy-start=1565282141\n" \
"backup-timestamp-start=1565282140\n" \
"backup-timestamp-stop=1565282142\n" \
"backup-type=\"full\"\n" \
"\n" \
"[backup:db]\n" \
"db-catalog-version=201409291\n" \
"db-control-version=942\n" \
"db-id=1\n" \
"db-system-id=1000000000000000094\n" \
"db-version=\"9.4\"\n" \
"\n" \
"[backup:option]\n" \
"option-archive-check=true\n" \
"option-archive-copy=true\n" \
"option-backup-standby=false\n" \
"option-buffer-size=16384\n" \
"option-checksum-page=true\n" \
"option-compress=false\n" \
"option-compress-level=3\n" \
"option-compress-level-network=3\n" \
"option-delta=false\n" \
"option-hardlink=false\n" \
"option-online=false\n" \
"option-process-max=32\n"
#define TEST_MANIFEST_TARGET \
"\n" \
"[backup:target]\n" \
"pg_data={\"path\":\"/pg/base\",\"type\":\"path\"}\n" \
"pg_data/base/1={\"path\":\"../../base-1\",\"type\":\"link\"}\n" \
"pg_data/pg_hba.conf={\"file\":\"pg_hba.conf\",\"path\":\"../pg_config\",\"type\":\"link\"}\n" \
"pg_data/pg_stat={\"path\":\"../pg_stat\",\"type\":\"link\"}\n" \
"pg_data/postgresql.conf={\"file\":\"postgresql.conf\",\"path\":\"../pg_config\",\"type\":\"link\"}\n" \
"pg_tblspc/1={\"path\":\"/tblspc/ts1\",\"tablespace-id\":\"1\",\"tablespace-name\":\"ts1\",\"type\":\"link\"}\n"
#define TEST_MANIFEST_DB \
"\n" \
"[db]\n" \
"mail={\"db-id\":16456,\"db-last-system-id\":12168}\n" \
"postgres={\"db-id\":12173,\"db-last-system-id\":12168}\n" \
"template0={\"db-id\":12168,\"db-last-system-id\":12168}\n" \
"template1={\"db-id\":1,\"db-last-system-id\":12168}\n" \
#define TEST_MANIFEST_FILE \
"\n" \
"[target:file]\n" \
"pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"master\":true" \
",\"reference\":\"20190818-084502F_20190819-084506D\",\"size\":4,\"timestamp\":1565282114}\n" \
"pg_data/base/16384/17000={\"checksum\":\"e0101dd8ffb910c9c202ca35b5f828bcb9697bed\",\"checksum-page\":false" \
",\"checksum-page-error\":[1],\"repo-size\":4096,\"size\":8192,\"timestamp\":1565282114}\n" \
"pg_data/base/16384/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"group\":false,\"size\":4" \
",\"timestamp\":1565282115}\n" \
"pg_data/base/32768/33000={\"checksum\":\"7a16d165e4775f7c92e8cdf60c0af57313f0bf90\",\"checksum-page\":true" \
",\"reference\":\"20190818-084502F\",\"size\":1073741824,\"timestamp\":1565282116}\n" \
"pg_data/base/32768/33000.32767={\"checksum\":\"6e99b589e550e68e934fd235ccba59fe5b592a9e\",\"checksum-page\":true" \
",\"reference\":\"20190818-084502F\",\"size\":32768,\"timestamp\":1565282114}\n" \
"pg_data/postgresql.conf={\"checksum\":\"6721d92c9fcdf4248acff1f9a1377127d9064807\",\"master\":true,\"size\":4457" \
",\"timestamp\":1565282114}\n" \
"pg_data/special={\"master\":true,\"mode\":\"0640\",\"size\":0,\"timestamp\":1565282120,\"user\":false}\n"
#define TEST_MANIFEST_FILE_DEFAULT \
"\n" \
"[target:file:default]\n" \
"group=\"group1\"\n" \
"master=false\n" \
"mode=\"0600\"\n" \
"user=\"user1\"\n"
#define TEST_MANIFEST_LINK \
"\n" \
"[target:link]\n" \
"pg_data/pg_stat={\"destination\":\"../pg_stat\"}\n" \
"pg_data/postgresql.conf={\"destination\":\"../pg_config/postgresql.conf\",\"group\":false,\"user\":\"user1\"}\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\":\"user2\"}\n" \
"pg_data/base={\"group\":\"group2\"}\n" \
"pg_data/base/16384={\"mode\":\"0750\"}\n" \
"pg_data/base/32768={}\n" \
"pg_data/base/65536={\"user\":false}\n"
#define TEST_MANIFEST_PATH_DEFAULT \
"\n" \
"[target:path:default]\n" \
"group=false\n" \
"mode=\"0700\"\n" \
"user=\"user1\"\n"
contentLoad = harnessInfoChecksumZ
(
TEST_MANIFEST_HEADER
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
);
TEST_ASSIGN(manifest, manifestNewLoad(ioBufferReadNew(contentLoad)), "load manifest");
TEST_RESULT_STR_Z(manifestPgPath(STRDEF("pg_data")), NULL, "check pg_data path");
TEST_RESULT_STR_Z(manifestPgPath(STRDEF("pg_data/PG_VERSION")), "PG_VERSION", "check pg_data path/file");
TEST_RESULT_STR_Z(manifestPgPath(STRDEF("pg_tblspc/1")), "pg_tblspc/1", "check pg_tblspc path/file");
TEST_RESULT_PTR(manifestCipherSubPass(manifest), NULL, " check cipher subpass");
// Absolute target paths
TEST_RESULT_STR_Z(manifestTargetPath(manifest, manifestTargetBase(manifest)), "/pg/base", "base target path");
TEST_RESULT_STR_Z(
manifestTargetPath(manifest, manifestTargetFind(manifest, STRDEF("pg_data/pg_hba.conf"))), "/pg/pg_config",
"relative file link target path");
TEST_RESULT_STR_Z(
manifestTargetPath(manifest, manifestTargetFind(manifest, STRDEF("pg_data/pg_stat"))), "/pg/pg_stat",
"relative path link target path");
TEST_RESULT_STR_Z(
manifestTargetPath(manifest, manifestTargetFind(manifest, STRDEF("pg_data/base/1"))), "/pg/base-1",
"relative path link target path");
// Link check
TEST_RESULT_VOID(manifestLinkCheck(manifest), "successful link check");
manifestTargetAdd(
manifest, &(ManifestTarget){
.name = STRDEF("pg_data/base/2"), .type = manifestTargetTypeLink, .path = STRDEF("../../base-1/base-2")});
TEST_ERROR(
manifestLinkCheck(manifest), LinkDestinationError,
"link 'base/2' (/pg/base-1/base-2) destination is a subdirectory of or the same directory as"
" link 'base/1' (/pg/base-1)");
manifestTargetRemove(manifest, STRDEF("pg_data/base/2"));
// ManifestFile getters
const ManifestFile *file = NULL;
TEST_ERROR(
manifestFileFind(manifest, STRDEF("bogus")), AssertError, "unable to find 'bogus' in manifest file list");
TEST_ASSIGN(file, manifestFileFind(manifest, STRDEF("pg_data/PG_VERSION")), "manifestFileFind()");
TEST_RESULT_STR_Z(file->name, "pg_data/PG_VERSION", " find file");
TEST_RESULT_STR_Z(
manifestFileFindDefault(manifest, STRDEF("bogus"), file)->name, "pg_data/PG_VERSION",
"manifestFileFindDefault() - return default");
TEST_RESULT_STR_Z(
manifestFileFindDefault(manifest, STRDEF("pg_data/special"), file)->name, "pg_data/special",
"manifestFileFindDefault() - return found");
TEST_ASSIGN(file, manifestFileFindDefault(manifest, STRDEF("bogus"), NULL), "manifestFileFindDefault()");
TEST_RESULT_PTR(file, NULL, " return default NULL");
// ManifestDb getters
const ManifestDb *db = NULL;
TEST_ERROR(
manifestDbFind(manifest, STRDEF("bogus")), AssertError, "unable to find 'bogus' in manifest db list");
TEST_ASSIGN(db, manifestDbFind(manifest, STRDEF("postgres")), "manifestDbFind()");
TEST_RESULT_STR_Z(db->name, "postgres", " check name");
TEST_RESULT_STR_Z(
manifestDbFindDefault(manifest, STRDEF("bogus"), db)->name, "postgres", "manifestDbFindDefault() - return default");
TEST_RESULT_UINT(
manifestDbFindDefault(manifest, STRDEF("template0"), db)->id, 12168, "manifestDbFindDefault() - return found");
TEST_ASSIGN(db, manifestDbFindDefault(manifest, STRDEF("bogus"), NULL), "manifestDbFindDefault()");
TEST_RESULT_PTR(db, NULL, " return default NULL");
// ManifestLink getters
const ManifestLink *link = NULL;
TEST_ERROR(
manifestLinkFind(manifest, STRDEF("bogus")), AssertError, "unable to find 'bogus' in manifest link list");
TEST_ASSIGN(link, manifestLinkFind(manifest, STRDEF("pg_data/pg_stat")), "find link");
TEST_RESULT_VOID(manifestLinkUpdate(manifest, STRDEF("pg_data/pg_stat"), STRDEF("../pg_stat")), " no update");
TEST_RESULT_STR_Z(link->destination, "../pg_stat", " check link");
TEST_RESULT_VOID(manifestLinkUpdate(manifest, STRDEF("pg_data/pg_stat"), STRDEF("../pg_stat2")), " update");
TEST_RESULT_STR_Z(link->destination, "../pg_stat2", " check link");
TEST_RESULT_VOID(manifestLinkUpdate(manifest, STRDEF("pg_data/pg_stat"), STRDEF("../pg_stat")), " fix link destination");
TEST_RESULT_STR_Z(
manifestLinkFindDefault(manifest, STRDEF("bogus"), link)->name, "pg_data/pg_stat",
"manifestLinkFindDefault() - return default");
TEST_RESULT_STR_Z(
manifestLinkFindDefault(manifest, STRDEF("pg_data/postgresql.conf"), link)->destination, "../pg_config/postgresql.conf",
"manifestLinkFindDefault() - return found");
TEST_ASSIGN(link, manifestLinkFindDefault(manifest, STRDEF("bogus"), NULL), "manifestLinkFindDefault()");
TEST_RESULT_PTR(link, NULL, " return default NULL");
// ManifestPath getters
const ManifestPath *path = NULL;
TEST_ERROR(
manifestPathFind(manifest, STRDEF("bogus")), AssertError, "unable to find 'bogus' in manifest path list");
TEST_ASSIGN(path, manifestPathFind(manifest, STRDEF("pg_data")), "manifestPathFind()");
TEST_RESULT_STR_Z(path->name, "pg_data", " check path");
TEST_RESULT_STR_Z(
manifestPathFindDefault(manifest, STRDEF("bogus"), path)->name, "pg_data",
"manifestPathFindDefault() - return default");
TEST_RESULT_STR_Z(
manifestPathFindDefault(manifest, STRDEF("pg_data/base"), path)->group, "group2",
"manifestPathFindDefault() - return found");
TEST_ASSIGN(path, manifestPathFindDefault(manifest, STRDEF("bogus"), NULL), "manifestPathFindDefault()");
TEST_RESULT_PTR(path, NULL, " return default NULL");
const ManifestTarget *target = NULL;
TEST_ASSIGN(target, manifestTargetFind(manifest, STRDEF("pg_data/pg_hba.conf")), "find target");
TEST_RESULT_VOID(
manifestTargetUpdate(manifest, target->name, target->path, STRDEF("pg_hba2.conf")), " update target file");
TEST_RESULT_STR_Z(target->file, "pg_hba2.conf", " check target file");
TEST_RESULT_VOID(manifestTargetUpdate(manifest, target->name, target->path, STRDEF("pg_hba.conf")), " fix target file");
contentSave = bufNew(0);
TEST_RESULT_VOID(manifestSave(manifest, ioBufferWriteNew(contentSave)), "save manifest");
TEST_RESULT_STR_STR(strNewBuf(contentSave), strNewBuf(contentLoad), " check save");
TEST_RESULT_VOID(manifestFileRemove(manifest, STRDEF("pg_data/PG_VERSION")), "remove file");
TEST_ERROR(
manifestFileRemove(manifest, STRDEF("pg_data/PG_VERSION")), AssertError,
"unable to remove 'pg_data/PG_VERSION' from manifest file list");
TEST_RESULT_VOID(manifestLinkRemove(manifest, STRDEF("pg_data/pg_stat")), "remove link");
TEST_ERROR(
manifestLinkRemove(manifest, STRDEF("pg_data/pg_stat")), AssertError,
"unable to remove 'pg_data/pg_stat' from manifest link list");
TEST_RESULT_VOID(manifestTargetRemove(manifest, STRDEF("pg_data/pg_hba.conf")), "remove target");
TEST_ERROR(
manifestTargetRemove(manifest, STRDEF("pg_data/pg_hba.conf")), AssertError,
"unable to remove 'pg_data/pg_hba.conf' from manifest target list");
}
// *****************************************************************************************************************************
if (testBegin("manifestLoadFile()"))
{
Manifest *manifest = NULL;
TEST_ERROR_FMT(
manifestLoadFile(storageTest, BACKUP_MANIFEST_FILE_STR, cipherTypeNone, NULL), FileMissingError,
"unable to load backup manifest file '%s/backup.manifest' or '%s/backup.manifest.copy':\n"
"FileMissingError: unable to open missing file '%s/backup.manifest' for read\n"
"FileMissingError: unable to open missing file '%s/backup.manifest.copy' for read",
testPath(), testPath(), testPath(), testPath());
// Also use this test to check that extra sections/keys are ignored using coverage.
// -------------------------------------------------------------------------------------------------------------------------
const Buffer *content = harnessInfoChecksumZ
(
"[backup]\n"
"backup-label=\"20190808-163540F\"\n"
"backup-timestamp-copy-start=1565282141\n"
"backup-timestamp-start=1565282140\n"
"backup-timestamp-stop=1565282142\n"
"backup-type=\"full\"\n"
"ignore-key=ignore-value\n"
"\n"
"[backup:db]\n"
"db-catalog-version=201409291\n"
"db-control-version=942\n"
"db-id=1\n"
"db-system-id=1000000000000000094\n"
"db-version=\"9.4\"\n"
"ignore-key=ignore-value\n"
"\n"
"[backup:option]\n"
"ignore-key=ignore-value\n"
"option-archive-check=true\n"
"option-archive-copy=true\n"
"option-compress=false\n"
"option-hardlink=false\n"
"option-online=false\n"
"\n"
"[backup:target]\n"
"pg_data={\"path\":\"/pg/base\",\"type\":\"path\"}\n"
"\n"
"[ignore-section]\n"
"ignore-key=ignore-value\n"
"\n"
"[target:file]\n"
"pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"size\":4,\"timestamp\":1565282114}\n"
"\n"
"[target:file:default]\n"
"group=\"group1\"\n"
"ignore-key=ignore-value\n"
"master=true\n"
"mode=\"0600\"\n"
"user=\"user1\"\n"
"\n"
"[target:link:default]\n"
"ignore-key=ignore-value\n"
"\n"
"[target:path]\n"
"pg_data={}\n"
"\n"
"[target:path:default]\n"
"group=\"group1\"\n"
"ignore-key=ignore-value\n"
"mode=\"0700\"\n"
"user=\"user1\"\n"
);
TEST_RESULT_VOID(
storagePutNP(storageNewWriteNP(storageTest, strNew(BACKUP_MANIFEST_FILE INFO_COPY_EXT)), content), "write copy");
TEST_ASSIGN(manifest, manifestLoadFile(storageTest, STRDEF(BACKUP_MANIFEST_FILE), cipherTypeNone, NULL), "load copy");
TEST_RESULT_UINT(manifestData(manifest)->pgSystemId, 1000000000000000094, " check file loaded");
storageRemoveP(storageTest, strNew(BACKUP_MANIFEST_FILE INFO_COPY_EXT), .errorOnMissing = true);
TEST_RESULT_VOID(
storagePutNP(storageNewWriteNP(storageTest, BACKUP_MANIFEST_FILE_STR), content), "write main");
TEST_ASSIGN(manifest, manifestLoadFile(storageTest, STRDEF(BACKUP_MANIFEST_FILE), cipherTypeNone, NULL), "load main");
TEST_RESULT_UINT(manifestData(manifest)->pgSystemId, 1000000000000000094, " check file loaded");
}
// Load/save a larger manifest to test performance and memory usage. The default sizing is for a "typical" cluster but this can
// be scaled to test larger cluster sizes.
// *****************************************************************************************************************************
if (testBegin("manifestNewLoad()/manifestSave() performance"))
{
Manifest *manifest = NULL;
// Manifest with all features
// -------------------------------------------------------------------------------------------------------------------------
String *manifestStr = strNew
(
"[backup]\n"
"backup-label=\"20190818-084502F_20190820-084502D\"\n"
"backup-prior=\"20190818-084502F\"\n"
"backup-timestamp-copy-start=1566290707\n"
"backup-timestamp-start=1566290702\n"
"backup-timestamp-stop=1566290710\n"
"backup-type=\"diff\"\n"
"\n"
"[backup:db]\n"
"db-catalog-version=201809051\n"
"db-control-version=1100\n"
"db-id=2\n"
"db-system-id=6689162560678426440\n"
"db-version=\"11\"\n"
"\n"
"[backup:option]\n"
"option-archive-check=true\n"
"option-archive-copy=false\n"
"option-backup-standby=false\n"
"option-buffer-size=4194304\n"
"option-checksum-page=true\n"
"option-compress=true\n"
"option-compress-level=9\n"
"option-compress-level-network=3\n"
"option-delta=false\n"
"option-hardlink=false\n"
"option-online=false\n"
"option-process-max=2\n"
"\n"
"[backup:target]\n"
"pg_data={\"path\":\"/pg/base\",\"type\":\"path\"}\n");
for (unsigned int linkIdx = 0; linkIdx < 1; linkIdx++)
strCatFmt(manifestStr, "pg_data/pg_stat%u={\"path\":\"../pg_stat\",\"type\":\"link\"}\n", linkIdx);
strCat(
manifestStr,
"\n"
"[target:file]\n");
for (unsigned int fileIdx = 0; fileIdx < 10; fileIdx++)
strCatFmt(
manifestStr,
"pg_data/base/16384/%u={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"size\":16384"
",\"timestamp\":1565282114}\n",
16384 + fileIdx);
strCat(
manifestStr,
"\n"
"[target:file:default]\n"
"group=\"postgres\"\n"
"master=false\n"
"mode=\"0600\"\n"
"user=\"postgres\"\n"
"\n"
"[target:link]\n"
"pg_data/pg_stat={\"destination\":\"../pg_stat\"}\n"
"\n"
"[target:link:default]\n"
"group=\"postgres\"\n"
"user=\"postgres\"\n"
"\n"
"[target:path]\n"
"pg_data={}\n"
"pg_data/base={}\n"
"pg_data/base/1={}\n"
"pg_data/base/13124={}\n"
"pg_data/base/13125={}\n"
"pg_data/base/16391={}\n"
"pg_data/global={}\n"
"pg_data/pg_commit_ts={}\n"
"pg_data/pg_dynshmem={}\n"
"pg_data/pg_logical={}\n"
"pg_data/pg_logical/mappings={}\n"
"pg_data/pg_logical/snapshots={}\n"
"pg_data/pg_multixact={}\n"
"pg_data/pg_multixact/members={}\n"
"pg_data/pg_multixact/offsets={}\n"
"pg_data/pg_notify={}\n"
"pg_data/pg_replslot={}\n"
"pg_data/pg_serial={}\n"
"pg_data/pg_snapshots={}\n"
"pg_data/pg_stat={}\n"
"pg_data/pg_stat_tmp={}\n"
"pg_data/pg_subtrans={}\n"
"pg_data/pg_tblspc={}\n"
"pg_data/pg_twophase={}\n"
"pg_data/pg_wal={}\n"
"pg_data/pg_wal/archive_status={}\n"
"pg_data/pg_xact={}\n"
"\n"
"[target:path:default]\n"
"group=\"postgres\"\n"
"mode=\"0700\"\n"
"user=\"postgres\"\n"
);
const Buffer *contentLoad = harnessInfoChecksum(manifestStr);
TEST_ASSIGN(manifest, manifestNewLoad(ioBufferReadNew(contentLoad)), "load manifest");
(void)manifest;
Buffer *contentSave = bufNew(0);
TEST_RESULT_VOID(manifestSave(manifest, ioBufferWriteNew(contentSave)), "save manifest");
if (!bufEq(contentSave, contentLoad))
{
TEST_RESULT_VOID( // {uncovered - only for debugging}
storagePutNP(storageNewWriteNP(storageTest, strNew(BACKUP_MANIFEST_FILE ".expected")), contentLoad),
"write expected manifest");
TEST_RESULT_VOID( // {uncovered - only for debugging}
storagePutNP(storageNewWriteNP(storageTest, strNew(BACKUP_MANIFEST_FILE ".actual")), contentSave),
"write actual manifest");
}
TEST_RESULT_BOOL(bufEq(contentSave, contentLoad), true, " check save");
}
}