You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-07 00:35:37 +02:00
Allow page header checks to be skipped.
These checks cause false negatives for page checksum verification when the page is encrypted because pd_upper might end up as 0 in the encrypted data. This issue is rare but reproducible given a large enough cluster. Make these checks optional, but leave them enabled by default.
This commit is contained in:
@ -17,6 +17,16 @@
|
|||||||
<release date="XXXX-XX-XX" version="2.46dev" title="UNDER DEVELOPMENT">
|
<release date="XXXX-XX-XX" version="2.46dev" title="UNDER DEVELOPMENT">
|
||||||
<release-core-list>
|
<release-core-list>
|
||||||
<release-improvement-list>
|
<release-improvement-list>
|
||||||
|
<release-item>
|
||||||
|
<release-item-contributor-list>
|
||||||
|
<release-item-ideator id="david.christensen"/>
|
||||||
|
<release-item-contributor id="david.steele"/>
|
||||||
|
<release-item-reviewer id="david.christensen"/>
|
||||||
|
</release-item-contributor-list>
|
||||||
|
|
||||||
|
<p>Allow page header checks to be skipped.</p>
|
||||||
|
</release-item>
|
||||||
|
|
||||||
<release-item>
|
<release-item>
|
||||||
<commit subject="Move error modules to common/error directory."/>
|
<commit subject="Move error modules to common/error directory."/>
|
||||||
<commit subject="Improve retry error messages."/>
|
<commit subject="Improve retry error messages."/>
|
||||||
|
@ -1210,6 +1210,15 @@ option:
|
|||||||
command-role:
|
command-role:
|
||||||
main: {}
|
main: {}
|
||||||
|
|
||||||
|
page-header-check:
|
||||||
|
section: global
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
command:
|
||||||
|
backup: {}
|
||||||
|
command-role:
|
||||||
|
main: {}
|
||||||
|
|
||||||
exclude:
|
exclude:
|
||||||
section: global
|
section: global
|
||||||
type: list
|
type: list
|
||||||
|
@ -1249,6 +1249,18 @@
|
|||||||
<example>y</example>
|
<example>y</example>
|
||||||
</config-key>
|
</config-key>
|
||||||
|
|
||||||
|
<config-key id="page-header-check" name="Page Header Check">
|
||||||
|
<summary>Check PostgreSQL page headers.</summary>
|
||||||
|
|
||||||
|
<text>
|
||||||
|
<p>Enabled by default, this option adds page header checks.</p>
|
||||||
|
|
||||||
|
<p>Disabling this option should be avoided except when necessary, e.g. if pages are encrypted.</p>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<example>n</example>
|
||||||
|
</config-key>
|
||||||
|
|
||||||
<config-key id="resume" name="Resume">
|
<config-key id="resume" name="Resume">
|
||||||
<summary>Allow resume of failed backup.</summary>
|
<summary>Allow resume of failed backup.</summary>
|
||||||
|
|
||||||
|
@ -1987,6 +1987,7 @@ backupJobCallback(void *const data, const unsigned int clientIdx)
|
|||||||
pckWriteBoolP(param, !backupProcessFilePrimary(jobData->standbyExp, file.name));
|
pckWriteBoolP(param, !backupProcessFilePrimary(jobData->standbyExp, file.name));
|
||||||
pckWriteBinP(param, file.checksumSha1 != NULL ? BUF(file.checksumSha1, HASH_TYPE_SHA1_SIZE) : NULL);
|
pckWriteBinP(param, file.checksumSha1 != NULL ? BUF(file.checksumSha1, HASH_TYPE_SHA1_SIZE) : NULL);
|
||||||
pckWriteBoolP(param, file.checksumPage);
|
pckWriteBoolP(param, file.checksumPage);
|
||||||
|
pckWriteBoolP(param, cfgOptionBool(cfgOptPageHeaderCheck));
|
||||||
|
|
||||||
// If block incremental then provide the location of the prior map when available
|
// If block incremental then provide the location of the prior map when available
|
||||||
if (blockIncr)
|
if (blockIncr)
|
||||||
|
@ -208,7 +208,8 @@ backupFile(
|
|||||||
ioFilterGroupAdd(
|
ioFilterGroupAdd(
|
||||||
ioReadFilterGroup(storageReadIo(read)),
|
ioReadFilterGroup(storageReadIo(read)),
|
||||||
pageChecksumNew(
|
pageChecksumNew(
|
||||||
segmentNumber(file->pgFile), PG_SEGMENT_PAGE_DEFAULT, storagePathP(storagePg(), file->pgFile)));
|
segmentNumber(file->pgFile), PG_SEGMENT_PAGE_DEFAULT, file->pgFilePageHeaderCheck,
|
||||||
|
storagePathP(storagePg(), file->pgFile)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compress filter
|
// Compress filter
|
||||||
|
@ -33,6 +33,7 @@ typedef struct BackupFile
|
|||||||
bool pgFileCopyExactSize; // Copy only pg expected size
|
bool pgFileCopyExactSize; // Copy only pg expected size
|
||||||
const Buffer *pgFileChecksum; // Expected pg file checksum
|
const Buffer *pgFileChecksum; // Expected pg file checksum
|
||||||
bool pgFileChecksumPage; // Validate page checksums?
|
bool pgFileChecksumPage; // Validate page checksums?
|
||||||
|
bool pgFilePageHeaderCheck; // Validate page headers?
|
||||||
size_t blockIncrSize; // Perform block incremental on this file?
|
size_t blockIncrSize; // Perform block incremental on this file?
|
||||||
size_t blockIncrChecksumSize; // Block checksum size
|
size_t blockIncrChecksumSize; // Block checksum size
|
||||||
uint64_t blockIncrSuperSize; // Size of the super block
|
uint64_t blockIncrSuperSize; // Size of the super block
|
||||||
|
@ -21,6 +21,7 @@ typedef struct PageChecksum
|
|||||||
{
|
{
|
||||||
unsigned int segmentPageTotal; // Total pages in a segment
|
unsigned int segmentPageTotal; // Total pages in a segment
|
||||||
unsigned int pageNoOffset; // Page number offset for subsequent segments
|
unsigned int pageNoOffset; // Page number offset for subsequent segments
|
||||||
|
bool headerCheck; // Perform additional header checks?
|
||||||
const String *fileName; // Used to load the file to retry pages
|
const String *fileName; // Used to load the file to retry pages
|
||||||
|
|
||||||
unsigned char *pageBuffer; // Buffer to hold a page while verifying the checksum
|
unsigned char *pageBuffer; // Buffer to hold a page while verifying the checksum
|
||||||
@ -99,28 +100,29 @@ pageChecksumProcess(THIS_VOID, const Buffer *const input)
|
|||||||
// Only validate page checksum if the page is complete
|
// Only validate page checksum if the page is complete
|
||||||
if (this->align || pageIdx < pageTotal - 1)
|
if (this->align || pageIdx < pageTotal - 1)
|
||||||
{
|
{
|
||||||
// Skip new pages indicated by pd_upper == 0
|
bool pageValid = true;
|
||||||
bool pdUpperValid = true;
|
|
||||||
|
|
||||||
if (pageHeader->pd_upper == 0)
|
// Skip new all-zero pages. To speed this up first check pd_upper when header check is enabled or pd_checksum when
|
||||||
|
// header check is disabled. The latter is required when the page is encrypted.
|
||||||
|
if ((this->headerCheck && pageHeader->pd_upper == 0) || (!this->headerCheck && pageHeader->pd_checksum == 0))
|
||||||
{
|
{
|
||||||
// Check that the entire page is zero in case pd_upper is corrupted
|
// Check that the entire page is zero
|
||||||
for (unsigned int pageIdx = 0; pageIdx < PG_PAGE_SIZE_DEFAULT / sizeof(size_t); pageIdx++)
|
for (unsigned int pageIdx = 0; pageIdx < PG_PAGE_SIZE_DEFAULT / sizeof(size_t); pageIdx++)
|
||||||
{
|
{
|
||||||
if (((size_t *)pageHeader)[pageIdx] != 0)
|
if (((size_t *)pageHeader)[pageIdx] != 0)
|
||||||
{
|
{
|
||||||
pdUpperValid = false;
|
pageValid = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the entire page is zero it is valid
|
// If the entire page is zero it is valid
|
||||||
if (pdUpperValid)
|
if (pageValid)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only validate the checksum if pd_upper is non-zero to avoid an assertion from pg_checksum_page()
|
// Only validate the checksum if the page is valid
|
||||||
if (pdUpperValid)
|
if (pageValid)
|
||||||
{
|
{
|
||||||
// Make a copy of the page since it will be modified by the page checksum function
|
// Make a copy of the page since it will be modified by the page checksum function
|
||||||
memcpy(this->pageBuffer, pageHeader, PG_PAGE_SIZE_DEFAULT);
|
memcpy(this->pageBuffer, pageHeader, PG_PAGE_SIZE_DEFAULT);
|
||||||
@ -223,11 +225,13 @@ pageChecksumResult(THIS_VOID)
|
|||||||
|
|
||||||
/**********************************************************************************************************************************/
|
/**********************************************************************************************************************************/
|
||||||
FN_EXTERN IoFilter *
|
FN_EXTERN IoFilter *
|
||||||
pageChecksumNew(const unsigned int segmentNo, const unsigned int segmentPageTotal, const String *const fileName)
|
pageChecksumNew(
|
||||||
|
const unsigned int segmentNo, const unsigned int segmentPageTotal, const bool headerCheck, const String *const fileName)
|
||||||
{
|
{
|
||||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||||
FUNCTION_LOG_PARAM(UINT, segmentNo);
|
FUNCTION_LOG_PARAM(UINT, segmentNo);
|
||||||
FUNCTION_LOG_PARAM(UINT, segmentPageTotal);
|
FUNCTION_LOG_PARAM(UINT, segmentPageTotal);
|
||||||
|
FUNCTION_LOG_PARAM(BOOL, headerCheck);
|
||||||
FUNCTION_LOG_PARAM(STRING, fileName);
|
FUNCTION_LOG_PARAM(STRING, fileName);
|
||||||
FUNCTION_LOG_END();
|
FUNCTION_LOG_END();
|
||||||
|
|
||||||
@ -237,6 +241,7 @@ pageChecksumNew(const unsigned int segmentNo, const unsigned int segmentPageTota
|
|||||||
{
|
{
|
||||||
.segmentPageTotal = segmentPageTotal,
|
.segmentPageTotal = segmentPageTotal,
|
||||||
.pageNoOffset = segmentNo * segmentPageTotal,
|
.pageNoOffset = segmentNo * segmentPageTotal,
|
||||||
|
.headerCheck = headerCheck,
|
||||||
.fileName = strDup(fileName),
|
.fileName = strDup(fileName),
|
||||||
.pageBuffer = bufPtr(bufNew(PG_PAGE_SIZE_DEFAULT)),
|
.pageBuffer = bufPtr(bufNew(PG_PAGE_SIZE_DEFAULT)),
|
||||||
.valid = true,
|
.valid = true,
|
||||||
@ -254,6 +259,7 @@ pageChecksumNew(const unsigned int segmentNo, const unsigned int segmentPageTota
|
|||||||
|
|
||||||
pckWriteU32P(packWrite, segmentNo);
|
pckWriteU32P(packWrite, segmentNo);
|
||||||
pckWriteU32P(packWrite, segmentPageTotal);
|
pckWriteU32P(packWrite, segmentPageTotal);
|
||||||
|
pckWriteBoolP(packWrite, headerCheck);
|
||||||
pckWriteStrP(packWrite, fileName);
|
pckWriteStrP(packWrite, fileName);
|
||||||
pckWriteEndP(packWrite);
|
pckWriteEndP(packWrite);
|
||||||
|
|
||||||
@ -276,9 +282,10 @@ pageChecksumNewPack(const Pack *const paramList)
|
|||||||
PackRead *const paramListPack = pckReadNew(paramList);
|
PackRead *const paramListPack = pckReadNew(paramList);
|
||||||
const unsigned int segmentNo = pckReadU32P(paramListPack);
|
const unsigned int segmentNo = pckReadU32P(paramListPack);
|
||||||
const unsigned int segmentPageTotal = pckReadU32P(paramListPack);
|
const unsigned int segmentPageTotal = pckReadU32P(paramListPack);
|
||||||
|
const bool headerCheck = pckReadBoolP(paramListPack);
|
||||||
const String *const fileName = pckReadStrP(paramListPack);
|
const String *const fileName = pckReadStrP(paramListPack);
|
||||||
|
|
||||||
result = ioFilterMove(pageChecksumNew(segmentNo, segmentPageTotal, fileName), memContextPrior());
|
result = ioFilterMove(pageChecksumNew(segmentNo, segmentPageTotal, headerCheck, fileName), memContextPrior());
|
||||||
}
|
}
|
||||||
MEM_CONTEXT_TEMP_END();
|
MEM_CONTEXT_TEMP_END();
|
||||||
|
|
||||||
|
@ -16,7 +16,8 @@ Filter type constant
|
|||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Constructors
|
Constructors
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
FN_EXTERN IoFilter *pageChecksumNew(unsigned int segmentNo, unsigned int segmentPageTotal, const String *fileName);
|
FN_EXTERN IoFilter *pageChecksumNew(
|
||||||
|
unsigned int segmentNo, unsigned int segmentPageTotal, bool headerCheck, const String *fileName);
|
||||||
FN_EXTERN IoFilter *pageChecksumNewPack(const Pack *paramList);
|
FN_EXTERN IoFilter *pageChecksumNewPack(const Pack *paramList);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -49,6 +49,7 @@ backupFileProtocol(PackRead *const param, ProtocolServer *const server)
|
|||||||
file.pgFileCopyExactSize = pckReadBoolP(param);
|
file.pgFileCopyExactSize = pckReadBoolP(param);
|
||||||
file.pgFileChecksum = pckReadBinP(param);
|
file.pgFileChecksum = pckReadBinP(param);
|
||||||
file.pgFileChecksumPage = pckReadBoolP(param);
|
file.pgFileChecksumPage = pckReadBoolP(param);
|
||||||
|
file.pgFilePageHeaderCheck = pckReadBoolP(param);
|
||||||
file.blockIncrSize = (size_t)pckReadU64P(param);
|
file.blockIncrSize = (size_t)pckReadU64P(param);
|
||||||
|
|
||||||
if (file.blockIncrSize > 0)
|
if (file.blockIncrSize > 0)
|
||||||
|
@ -96,6 +96,7 @@ Option constants
|
|||||||
#define CFGOPT_NEUTRAL_UMASK "neutral-umask"
|
#define CFGOPT_NEUTRAL_UMASK "neutral-umask"
|
||||||
#define CFGOPT_ONLINE "online"
|
#define CFGOPT_ONLINE "online"
|
||||||
#define CFGOPT_OUTPUT "output"
|
#define CFGOPT_OUTPUT "output"
|
||||||
|
#define CFGOPT_PAGE_HEADER_CHECK "page-header-check"
|
||||||
#define CFGOPT_PG "pg"
|
#define CFGOPT_PG "pg"
|
||||||
#define CFGOPT_PG_VERSION_FORCE "pg-version-force"
|
#define CFGOPT_PG_VERSION_FORCE "pg-version-force"
|
||||||
#define CFGOPT_PROCESS "process"
|
#define CFGOPT_PROCESS "process"
|
||||||
@ -134,7 +135,7 @@ Option constants
|
|||||||
#define CFGOPT_TYPE "type"
|
#define CFGOPT_TYPE "type"
|
||||||
#define CFGOPT_VERBOSE "verbose"
|
#define CFGOPT_VERBOSE "verbose"
|
||||||
|
|
||||||
#define CFG_OPTION_TOTAL 166
|
#define CFG_OPTION_TOTAL 167
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Option value constants
|
Option value constants
|
||||||
@ -416,6 +417,7 @@ typedef enum
|
|||||||
cfgOptNeutralUmask,
|
cfgOptNeutralUmask,
|
||||||
cfgOptOnline,
|
cfgOptOnline,
|
||||||
cfgOptOutput,
|
cfgOptOutput,
|
||||||
|
cfgOptPageHeaderCheck,
|
||||||
cfgOptPg,
|
cfgOptPg,
|
||||||
cfgOptPgDatabase,
|
cfgOptPgDatabase,
|
||||||
cfgOptPgHost,
|
cfgOptPgHost,
|
||||||
|
@ -3225,6 +3225,32 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
|
|||||||
), // opt/output
|
), // opt/output
|
||||||
), // opt/output
|
), // opt/output
|
||||||
// -----------------------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------------------
|
||||||
|
PARSE_RULE_OPTION // opt/page-header-check
|
||||||
|
( // opt/page-header-check
|
||||||
|
PARSE_RULE_OPTION_NAME("page-header-check"), // opt/page-header-check
|
||||||
|
PARSE_RULE_OPTION_TYPE(cfgOptTypeBoolean), // opt/page-header-check
|
||||||
|
PARSE_RULE_OPTION_NEGATE(true), // opt/page-header-check
|
||||||
|
PARSE_RULE_OPTION_RESET(true), // opt/page-header-check
|
||||||
|
PARSE_RULE_OPTION_REQUIRED(true), // opt/page-header-check
|
||||||
|
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/page-header-check
|
||||||
|
// opt/page-header-check
|
||||||
|
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/page-header-check
|
||||||
|
( // opt/page-header-check
|
||||||
|
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/page-header-check
|
||||||
|
), // opt/page-header-check
|
||||||
|
// opt/page-header-check
|
||||||
|
PARSE_RULE_OPTIONAL // opt/page-header-check
|
||||||
|
( // opt/page-header-check
|
||||||
|
PARSE_RULE_OPTIONAL_GROUP // opt/page-header-check
|
||||||
|
( // opt/page-header-check
|
||||||
|
PARSE_RULE_OPTIONAL_DEFAULT // opt/page-header-check
|
||||||
|
( // opt/page-header-check
|
||||||
|
PARSE_RULE_VAL_BOOL_TRUE, // opt/page-header-check
|
||||||
|
), // opt/page-header-check
|
||||||
|
), // opt/page-header-check
|
||||||
|
), // opt/page-header-check
|
||||||
|
), // opt/page-header-check
|
||||||
|
// -----------------------------------------------------------------------------------------------------------------------------
|
||||||
PARSE_RULE_OPTION // opt/pg
|
PARSE_RULE_OPTION // opt/pg
|
||||||
( // opt/pg
|
( // opt/pg
|
||||||
PARSE_RULE_OPTION_NAME("pg"), // opt/pg
|
PARSE_RULE_OPTION_NAME("pg"), // opt/pg
|
||||||
@ -9875,6 +9901,7 @@ static const uint8_t optionResolveOrder[] =
|
|||||||
cfgOptNeutralUmask, // opt-resolve-order
|
cfgOptNeutralUmask, // opt-resolve-order
|
||||||
cfgOptOnline, // opt-resolve-order
|
cfgOptOnline, // opt-resolve-order
|
||||||
cfgOptOutput, // opt-resolve-order
|
cfgOptOutput, // opt-resolve-order
|
||||||
|
cfgOptPageHeaderCheck, // opt-resolve-order
|
||||||
cfgOptPg, // opt-resolve-order
|
cfgOptPg, // opt-resolve-order
|
||||||
cfgOptPgLocal, // opt-resolve-order
|
cfgOptPgLocal, // opt-resolve-order
|
||||||
cfgOptPgPath, // opt-resolve-order
|
cfgOptPgPath, // opt-resolve-order
|
||||||
|
@ -12,6 +12,7 @@ Modifications need to be made after copying:
|
|||||||
|
|
||||||
1) Remove `#include "storage/bufpage.h"`.
|
1) Remove `#include "storage/bufpage.h"`.
|
||||||
2) Make pg_checksum_page() static.
|
2) Make pg_checksum_page() static.
|
||||||
|
3) Remove Assert(!PageIsNew(&cpage->phdr)).
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
@ -204,9 +205,6 @@ pg_checksum_page(char *page, BlockNumber blkno)
|
|||||||
uint16 save_checksum;
|
uint16 save_checksum;
|
||||||
uint32 checksum;
|
uint32 checksum;
|
||||||
|
|
||||||
/* We only calculate the checksum for properly-initialized pages */
|
|
||||||
Assert(!PageIsNew(&cpage->phdr));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save pd_checksum and temporarily set it to zero, so that the checksum
|
* Save pd_checksum and temporarily set it to zero, so that the checksum
|
||||||
* calculation isn't affected by the old checksum stored on the page.
|
* calculation isn't affected by the old checksum stored on the page.
|
||||||
|
@ -205,14 +205,6 @@ typedef struct PageHeaderData
|
|||||||
// ---------------------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------------------
|
||||||
typedef PageHeaderData *PageHeader;
|
typedef PageHeaderData *PageHeader;
|
||||||
|
|
||||||
// PageIsNew macro
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
* PageIsNew
|
|
||||||
* returns true iff page has not been initialized (by PageInit)
|
|
||||||
*/
|
|
||||||
#define PageIsNew(page) (((PageHeader) (page))->pd_upper == 0)
|
|
||||||
|
|
||||||
/***********************************************************************************************************************************
|
/***********************************************************************************************************************************
|
||||||
Types from src/include/access/transam.h
|
Types from src/include/access/transam.h
|
||||||
***********************************************************************************************************************************/
|
***********************************************************************************************************************************/
|
||||||
|
@ -626,7 +626,7 @@ testRun(void)
|
|||||||
IoWrite *write = ioBufferWriteNew(bufferOut);
|
IoWrite *write = ioBufferWriteNew(bufferOut);
|
||||||
ioFilterGroupAdd(
|
ioFilterGroupAdd(
|
||||||
ioWriteFilterGroup(write),
|
ioWriteFilterGroup(write),
|
||||||
pageChecksumNewPack(ioFilterParamList(pageChecksumNew(0, PG_SEGMENT_PAGE_DEFAULT, STRDEF(BOGUS_STR)))));
|
pageChecksumNewPack(ioFilterParamList(pageChecksumNew(0, PG_SEGMENT_PAGE_DEFAULT, true, STRDEF(BOGUS_STR)))));
|
||||||
ioWriteOpen(write);
|
ioWriteOpen(write);
|
||||||
ioWrite(write, buffer);
|
ioWrite(write, buffer);
|
||||||
TEST_ERROR(ioWrite(write, buffer), AssertError, "should not be possible to see two misaligned pages in a row");
|
TEST_ERROR(ioWrite(write, buffer), AssertError, "should not be possible to see two misaligned pages in a row");
|
||||||
@ -656,7 +656,8 @@ testRun(void)
|
|||||||
|
|
||||||
write = ioBufferWriteNew(bufferOut);
|
write = ioBufferWriteNew(bufferOut);
|
||||||
ioFilterGroupAdd(
|
ioFilterGroupAdd(
|
||||||
ioWriteFilterGroup(write), pageChecksumNew(0, PG_SEGMENT_PAGE_DEFAULT, storagePathP(storageTest, STRDEF("relation"))));
|
ioWriteFilterGroup(write),
|
||||||
|
pageChecksumNew(0, PG_SEGMENT_PAGE_DEFAULT, true, storagePathP(storageTest, STRDEF("relation"))));
|
||||||
ioWriteOpen(write);
|
ioWriteOpen(write);
|
||||||
ioWrite(write, buffer);
|
ioWrite(write, buffer);
|
||||||
ioWriteClose(write);
|
ioWriteClose(write);
|
||||||
@ -3294,9 +3295,27 @@ testRun(void)
|
|||||||
hrnCfgArgRawZ(argList, cfgOptRepoRetentionFull, "1");
|
hrnCfgArgRawZ(argList, cfgOptRepoRetentionFull, "1");
|
||||||
hrnCfgArgRawStrId(argList, cfgOptType, backupTypeIncr);
|
hrnCfgArgRawStrId(argList, cfgOptType, backupTypeIncr);
|
||||||
hrnCfgArgRawBool(argList, cfgOptDelta, true);
|
hrnCfgArgRawBool(argList, cfgOptDelta, true);
|
||||||
|
hrnCfgArgRawBool(argList, cfgOptPageHeaderCheck, false);
|
||||||
hrnCfgArgRawBool(argList, cfgOptRepoHardlink, true);
|
hrnCfgArgRawBool(argList, cfgOptRepoHardlink, true);
|
||||||
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
||||||
|
|
||||||
|
// File with bad page checksum and header errors that will be ignored
|
||||||
|
Buffer *relation = bufNew(PG_PAGE_SIZE_DEFAULT * 4);
|
||||||
|
memset(bufPtr(relation), 0, bufSize(relation));
|
||||||
|
*(PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x00)) = (PageHeaderData){.pd_upper = 0xFF};
|
||||||
|
*(PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x01)) = (PageHeaderData){.pd_upper = 0x00};
|
||||||
|
*(PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x02)) = (PageHeaderData){.pd_upper = 0x00};
|
||||||
|
(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x02))[PG_PAGE_SIZE_DEFAULT - 1] = 0xFF;
|
||||||
|
((PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x02)))->pd_checksum = pgPageChecksum(
|
||||||
|
bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x02), 2);
|
||||||
|
*(PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x03)) = (PageHeaderData){.pd_upper = 0x00};
|
||||||
|
(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x03))[PG_PAGE_SIZE_DEFAULT - 1] = 0xEE;
|
||||||
|
((PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x03)))->pd_checksum = 1;
|
||||||
|
bufUsedSet(relation, bufSize(relation));
|
||||||
|
|
||||||
|
HRN_STORAGE_PUT(storagePgWrite(), PG_PATH_BASE "/1/3", relation, .timeModified = backupTimeStart);
|
||||||
|
const char *rel1_3Sha1 = strZ(strNewEncode(encodingHex, cryptoHashOne(hashTypeSha1, relation)));
|
||||||
|
|
||||||
// Run backup. Make sure that the timeline selected converts to hexdecimal that can't be interpreted as decimal.
|
// Run backup. Make sure that the timeline selected converts to hexdecimal that can't be interpreted as decimal.
|
||||||
hrnBackupPqScriptP(PG_VERSION_11, backupTimeStart, .timeline = 0x2C, .walTotal = 2);
|
hrnBackupPqScriptP(PG_VERSION_11, backupTimeStart, .timeline = 0x2C, .walTotal = 2);
|
||||||
TEST_RESULT_VOID(hrnCmdBackup(), "backup");
|
TEST_RESULT_VOID(hrnCmdBackup(), "backup");
|
||||||
@ -3308,6 +3327,8 @@ testRun(void)
|
|||||||
"P00 INFO: check archive for segment 0000002C05DB8EB000000000\n"
|
"P00 INFO: check archive for segment 0000002C05DB8EB000000000\n"
|
||||||
"P00 WARN: a timeline switch has occurred since the 20191027-181320F backup, enabling delta checksum\n"
|
"P00 WARN: a timeline switch has occurred since the 20191027-181320F backup, enabling delta checksum\n"
|
||||||
" HINT: this is normal after restoring from backup or promoting a standby.\n"
|
" HINT: this is normal after restoring from backup or promoting a standby.\n"
|
||||||
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/base/1/3 (32KB, [PCT]) checksum [SHA1]\n"
|
||||||
|
"P00 WARN: invalid page checksums found in file " TEST_PATH "/pg1/base/1/3 at pages 0, 3\n"
|
||||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (8KB, [PCT]) checksum [SHA1]\n"
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (8KB, [PCT]) checksum [SHA1]\n"
|
||||||
"P01 DETAIL: match file from prior backup " TEST_PATH "/pg1/base/1/1 (8KB, [PCT]) checksum [SHA1]\n"
|
"P01 DETAIL: match file from prior backup " TEST_PATH "/pg1/base/1/1 (8KB, [PCT]) checksum [SHA1]\n"
|
||||||
"P01 DETAIL: match file from prior backup " TEST_PATH "/pg1/postgresql.conf (11B, [PCT]) checksum [SHA1]\n"
|
"P01 DETAIL: match file from prior backup " TEST_PATH "/pg1/postgresql.conf (11B, [PCT]) checksum [SHA1]\n"
|
||||||
@ -3322,65 +3343,73 @@ testRun(void)
|
|||||||
"P00 DETAIL: wrote 'tablespace_map' file returned from backup stop function\n"
|
"P00 DETAIL: wrote 'tablespace_map' file returned from backup stop function\n"
|
||||||
"P00 INFO: check archive for segment(s) 0000002C05DB8EB000000000:0000002C05DB8EB000000001\n"
|
"P00 INFO: check archive for segment(s) 0000002C05DB8EB000000000:0000002C05DB8EB000000001\n"
|
||||||
"P00 INFO: new backup label = 20191027-181320F_20191030-014640I\n"
|
"P00 INFO: new backup label = 20191027-181320F_20191030-014640I\n"
|
||||||
"P00 INFO: incr backup size = [SIZE], file total = 7");
|
"P00 INFO: incr backup size = [SIZE], file total = 8");
|
||||||
|
|
||||||
TEST_RESULT_STR_Z(
|
TEST_RESULT_STR(
|
||||||
testBackupValidateP(storageRepo(), STRDEF(STORAGE_REPO_BACKUP "/latest")),
|
testBackupValidateP(storageRepo(), STRDEF(STORAGE_REPO_BACKUP "/latest")),
|
||||||
". {link, d=20191027-181320F_20191030-014640I}\n"
|
strNewFmt(
|
||||||
"pg_data {path}\n"
|
". {link, d=20191027-181320F_20191030-014640I}\n"
|
||||||
"pg_data/PG_VERSION.gz {file, s=2}\n"
|
"pg_data {path}\n"
|
||||||
"pg_data/backup_label.gz {file, s=17}\n"
|
"pg_data/PG_VERSION.gz {file, s=2}\n"
|
||||||
"pg_data/base {path}\n"
|
"pg_data/backup_label.gz {file, s=17}\n"
|
||||||
"pg_data/base/1 {path}\n"
|
"pg_data/base {path}\n"
|
||||||
"pg_data/base/1/1.gz {file, s=8192}\n"
|
"pg_data/base/1 {path}\n"
|
||||||
"pg_data/global {path}\n"
|
"pg_data/base/1/1.gz {file, s=8192}\n"
|
||||||
"pg_data/global/pg_control.gz {file, s=8192}\n"
|
"pg_data/base/1/3.gz {file, s=32768}\n"
|
||||||
"pg_data/pg_tblspc {path}\n"
|
"pg_data/global {path}\n"
|
||||||
"pg_data/pg_tblspc/32768 {link, d=../../pg_tblspc/32768}\n"
|
"pg_data/global/pg_control.gz {file, s=8192}\n"
|
||||||
"pg_data/pg_wal {path}\n"
|
"pg_data/pg_tblspc {path}\n"
|
||||||
"pg_data/postgresql.conf.gz {file, s=11}\n"
|
"pg_data/pg_tblspc/32768 {link, d=../../pg_tblspc/32768}\n"
|
||||||
"pg_data/tablespace_map.gz {file, s=19}\n"
|
"pg_data/pg_wal {path}\n"
|
||||||
"pg_tblspc {path}\n"
|
"pg_data/postgresql.conf.gz {file, s=11}\n"
|
||||||
"pg_tblspc/32768 {path}\n"
|
"pg_data/tablespace_map.gz {file, s=19}\n"
|
||||||
"pg_tblspc/32768/PG_11_201809051 {path}\n"
|
"pg_tblspc {path}\n"
|
||||||
"pg_tblspc/32768/PG_11_201809051/1 {path}\n"
|
"pg_tblspc/32768 {path}\n"
|
||||||
"pg_tblspc/32768/PG_11_201809051/1/5.gz {file, s=0}\n"
|
"pg_tblspc/32768/PG_11_201809051 {path}\n"
|
||||||
"--------\n"
|
"pg_tblspc/32768/PG_11_201809051/1 {path}\n"
|
||||||
"[backup:target]\n"
|
"pg_tblspc/32768/PG_11_201809051/1/5.gz {file, s=0}\n"
|
||||||
"pg_data={\"path\":\"" TEST_PATH "/pg1\",\"type\":\"path\"}\n"
|
"--------\n"
|
||||||
"pg_tblspc/32768={\"path\":\"../../pg1-tblspc/32768\",\"tablespace-id\":\"32768\""
|
"[backup:target]\n"
|
||||||
",\"tablespace-name\":\"tblspc32768\",\"type\":\"link\"}\n"
|
"pg_data={\"path\":\"" TEST_PATH "/pg1\",\"type\":\"path\"}\n"
|
||||||
"\n"
|
"pg_tblspc/32768={\"path\":\"../../pg1-tblspc/32768\",\"tablespace-id\":\"32768\""
|
||||||
"[target:file]\n"
|
",\"tablespace-name\":\"tblspc32768\",\"type\":\"link\"}\n"
|
||||||
"pg_data/PG_VERSION={\"checksum\":\"17ba0791499db908433b80f37c5fbc89b870084b\",\"reference\":\"20191027-181320F\""
|
"\n"
|
||||||
",\"size\":2,\"timestamp\":1572200000}\n"
|
"[target:file]\n"
|
||||||
"pg_data/backup_label={\"checksum\":\"8e6f41ac87a7514be96260d65bacbffb11be77dc\",\"size\":17"
|
"pg_data/PG_VERSION={\"checksum\":\"17ba0791499db908433b80f37c5fbc89b870084b\""
|
||||||
",\"timestamp\":1572400002}\n"
|
",\"reference\":\"20191027-181320F\",\"size\":2,\"timestamp\":1572200000}\n"
|
||||||
"pg_data/base/1/1={\"checksum\":\"0631457264ff7f8d5fb1edc2c0211992a67c73e6\",\"checksum-page\":true"
|
"pg_data/backup_label={\"checksum\":\"8e6f41ac87a7514be96260d65bacbffb11be77dc\",\"size\":17"
|
||||||
",\"reference\":\"20191027-181320F\",\"size\":8192,\"timestamp\":1572200000}\n"
|
",\"timestamp\":1572400002}\n"
|
||||||
"pg_data/global/pg_control={\"size\":8192,\"timestamp\":1572400000}\n"
|
"pg_data/base/1/1={\"checksum\":\"0631457264ff7f8d5fb1edc2c0211992a67c73e6\",\"checksum-page\":true"
|
||||||
"pg_data/postgresql.conf={\"checksum\":\"e3db315c260e79211b7b52587123b7aa060f30ab\""
|
",\"reference\":\"20191027-181320F\",\"size\":8192,\"timestamp\":1572200000}\n"
|
||||||
",\"reference\":\"20191027-181320F\",\"size\":11,\"timestamp\":1570000000}\n"
|
"pg_data/base/1/3={\"checksum\":\"%s\",\"checksum-page\":false,\"checksum-page-error\":[0,3],\"size\":32768"
|
||||||
"pg_data/tablespace_map={\"checksum\":\"87fe624d7976c2144e10afcb7a9a49b071f35e9c\",\"size\":19"
|
",\"timestamp\":1572400000}\n"
|
||||||
",\"timestamp\":1572400002}\n"
|
"pg_data/global/pg_control={\"size\":8192,\"timestamp\":1572400000}\n"
|
||||||
"pg_tblspc/32768/PG_11_201809051/1/5={\"checksum-page\":true,\"reference\":\"20191027-181320F\",\"size\":0"
|
"pg_data/postgresql.conf={\"checksum\":\"e3db315c260e79211b7b52587123b7aa060f30ab\""
|
||||||
",\"timestamp\":1572200000}\n"
|
",\"reference\":\"20191027-181320F\",\"size\":11,\"timestamp\":1570000000}\n"
|
||||||
"\n"
|
"pg_data/tablespace_map={\"checksum\":\"87fe624d7976c2144e10afcb7a9a49b071f35e9c\",\"size\":19"
|
||||||
"[target:link]\n"
|
",\"timestamp\":1572400002}\n"
|
||||||
"pg_data/pg_tblspc/32768={\"destination\":\"../../pg1-tblspc/32768\"}\n"
|
"pg_tblspc/32768/PG_11_201809051/1/5={\"checksum-page\":true,\"reference\":\"20191027-181320F\",\"size\":0"
|
||||||
"\n"
|
",\"timestamp\":1572200000}\n"
|
||||||
"[target:path]\n"
|
"\n"
|
||||||
"pg_data={}\n"
|
"[target:link]\n"
|
||||||
"pg_data/base={}\n"
|
"pg_data/pg_tblspc/32768={\"destination\":\"../../pg1-tblspc/32768\"}\n"
|
||||||
"pg_data/base/1={}\n"
|
"\n"
|
||||||
"pg_data/global={}\n"
|
"[target:path]\n"
|
||||||
"pg_data/pg_tblspc={}\n"
|
"pg_data={}\n"
|
||||||
"pg_data/pg_wal={}\n"
|
"pg_data/base={}\n"
|
||||||
"pg_tblspc={}\n"
|
"pg_data/base/1={}\n"
|
||||||
"pg_tblspc/32768={}\n"
|
"pg_data/global={}\n"
|
||||||
"pg_tblspc/32768/PG_11_201809051={}\n"
|
"pg_data/pg_tblspc={}\n"
|
||||||
"pg_tblspc/32768/PG_11_201809051/1={}\n",
|
"pg_data/pg_wal={}\n"
|
||||||
|
"pg_tblspc={}\n"
|
||||||
|
"pg_tblspc/32768={}\n"
|
||||||
|
"pg_tblspc/32768/PG_11_201809051={}\n"
|
||||||
|
"pg_tblspc/32768/PG_11_201809051/1={}\n",
|
||||||
|
rel1_3Sha1),
|
||||||
"compare file list");
|
"compare file list");
|
||||||
|
|
||||||
|
// Remove test files
|
||||||
|
HRN_STORAGE_REMOVE(storagePgWrite(), "base/1/3", .errorOnMissing = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
|
Reference in New Issue
Block a user