Fix checksum validation for relations that have more than one segment.

A blkno argument passed to pg_checksum_page procedure should be not a
relative block number in a given segment but an absolute block number
in the entire relation. It's the same for the segment number 0, but for
segment number N the difference is RELSEG_SIZE * N.

Aleksander Alekseev
This commit is contained in:
Teodor Sigaev 2016-09-07 19:27:02 +03:00
parent b163cdaa53
commit 052ed01129
3 changed files with 115 additions and 3 deletions

View File

@ -59,7 +59,7 @@ not require any manual adjustments of the Makefile.
------------------------------------------------------------------------
Invocation:
pg_filedump [-abcdfhikxy] [-R startblock [endblock]] [-S blocksize] file
pg_filedump [-abcdfhikxy] [-R startblock [endblock]] [-S blocksize] [-s segsize] [-n segnumber] file
Defaults are: relative addressing, range of the entire file, block size
as listed on block 0 in the file
@ -80,6 +80,8 @@ The following options are valid for heap and index files:
[startblock]: block to start at
[endblock]: block to end at
A startblock without an endblock will format the single block
-s Force segment size to [segsize]
-n Force segment number to [segnumber]
-S Force block size to [blocksize]
-x Force interpreted formatting of block items as index items
-y Force interpreted formatting of block items as heap items

View File

@ -52,6 +52,12 @@ static unsigned int blockSize = 0;
/* Current block in file */
static unsigned int currentBlock = 0;
/* Segment size in bytes */
static unsigned int segmentSize = RELSEG_SIZE * BLCKSZ;
/* Number of current segment */
static unsigned int segmentNumber = 0;
/* Offset of current block */
static unsigned int pageOffset = 0;
@ -95,7 +101,7 @@ DisplayOptions(unsigned int validOptions)
FD_VERSION, FD_PG_VERSION);
printf
("\nUsage: pg_filedump [-abcdfhikxy] [-R startblock [endblock]] [-S blocksize] file\n\n"
("\nUsage: pg_filedump [-abcdfhikxy] [-R startblock [endblock]] [-S blocksize] [-s segsize] [-n segnumber] file\n\n"
"Display formatted contents of a PostgreSQL heap/index/control file\n"
"Defaults are: relative addressing, range of the entire file, block\n"
" size as listed on block 0 in the file\n\n"
@ -114,6 +120,8 @@ DisplayOptions(unsigned int validOptions)
" indexed from 0)\n" " [startblock]: block to start at\n"
" [endblock]: block to end at\n"
" A startblock without an endblock will format the single block\n"
" -s Force segment size to [segsize]\n"
" -n Force segment number to [segnumber]\n"
" -S Force block size to [blocksize]\n"
" -x Force interpreted formatting of block items as index items\n"
" -y Force interpreted formatting of block items as heap items\n\n"
@ -124,6 +132,31 @@ DisplayOptions(unsigned int validOptions)
"\nReport bugs to <pgsql-bugs@postgresql.org>\n");
}
/*
* Determine segment number by segment file name. For instance, if file
* name is /path/to/xxxx.7 procedure returns 7. Default return value is 0.
*/
static unsigned int
GetSegmentNumberFromFileName(const char* fileName)
{
int segnumOffset = strlen(fileName) - 1;
if(segnumOffset < 0)
return 0;
while(isdigit(fileName[segnumOffset]))
{
segnumOffset--;
if(segnumOffset < 0)
return 0;
}
if(fileName[segnumOffset] != '.')
return 0;
return atoi(&fileName[segnumOffset+1]);
}
/* Iterate through the provided options and set the option flags. */
/* An error will result in a positive rc and will force a display */
/* of the usage information. This routine returns enum */
@ -235,6 +268,69 @@ ConsumeOptions(int numOptions, char **options)
break;
}
}
/* Check for the special case where the user forces a segment size. */
else if ((optionStringLength == 2)
&& (strcmp(optionString, "-s") == 0))
{
int localSegmentSize;
SET_OPTION(segmentOptions, SEGMENT_SIZE_FORCED, 's');
/* Only accept the forced size option once */
if (rc == OPT_RC_DUPLICATE)
break;
/* The token immediately following -s is the segment size */
if (x >= (numOptions - 2))
{
rc = OPT_RC_INVALID;
printf("Error: Missing segment size identifier.\n");
break;
}
/* Next option encountered must be forced segment size */
optionString = options[++x];
if ((localSegmentSize = GetOptionValue(optionString)) > 0)
segmentSize = (unsigned int) localSegmentSize;
else
{
rc = OPT_RC_INVALID;
printf("Error: Invalid segment size requested <%s>.\n",
optionString);
break;
}
}
/* Check for the special case where the user forces a segment number */
/* instead of having the tool determine it by file name. */
else if ((optionStringLength == 2)
&& (strcmp(optionString, "-n") == 0))
{
int localSegmentNumber;
SET_OPTION(segmentOptions, SEGMENT_NUMBER_FORCED, 'n');
/* Only accept the forced segment number option once */
if (rc == OPT_RC_DUPLICATE)
break;
/* The token immediately following -n is the segment number */
if (x >= (numOptions - 2))
{
rc = OPT_RC_INVALID;
printf("Error: Missing segment number identifier.\n");
break;
}
/* Next option encountered must be forced segment number */
optionString = options[++x];
if ((localSegmentNumber = GetOptionValue(optionString)) > 0)
segmentNumber = (unsigned int) localSegmentNumber;
else
{
rc = OPT_RC_INVALID;
printf("Error: Invalid segment number requested <%s>.\n",
optionString);
break;
}
}
/* The last option MUST be the file name */
else if (x == (numOptions - 1))
{
@ -243,7 +339,11 @@ ConsumeOptions(int numOptions, char **options)
{
fp = fopen(optionString, "rb");
if (fp)
{
fileName = options[x];
if(!(segmentOptions & SEGMENT_NUMBER_FORCED))
segmentNumber = GetSegmentNumberFromFileName(fileName);
}
else
{
rc = OPT_RC_FILE;
@ -691,7 +791,8 @@ FormatHeader(Page page, BlockNumber blkno)
if (blockOptions & BLOCK_CHECKSUMS)
{
uint16 calc_checksum = pg_checksum_page(page, blkno);
uint32 delta = (segmentSize/blockSize)*segmentNumber;
uint16 calc_checksum = pg_checksum_page(page, delta + blkno);
if (calc_checksum != pageHeader->pd_checksum)
printf(" Error: checksum failure: calculated 0x%04x.\n\n",

View File

@ -55,6 +55,15 @@ typedef enum blockSwitches
BLOCK_CHECKSUMS = 0x00000040 /* -k: verify block checksums */
} blockSwitches;
/* Segment-related options */
static unsigned int segmentOptions = 0;
typedef enum segmentSwitches
{
SEGMENT_SIZE_FORCED = 0x00000001, /* -s: Segment size forced */
SEGMENT_NUMBER_FORCED = 0x00000002, /* -n: Segment number forced */
} segmentSwitches;
/* -R[start]:Block range start */
static int blockStart = -1;