Fix checksum validation for relations that have more than one segment.
authorTeodor Sigaev <teodor@sigaev.ru>
Wed, 7 Sep 2016 16:27:02 +0000 (19:27 +0300)
committerTeodor Sigaev <teodor@sigaev.ru>
Wed, 7 Sep 2016 16:27:02 +0000 (19:27 +0300)
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

README.pg_filedump
pg_filedump.c
pg_filedump.h

index 45843fde5f930b191646258da26d0b53d016560b..1c950ae155295de44b827821e97597ac7faeff3c 100644 (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
index 2179103ad06c6acbbe02d0141d1c89cc91a7ea86..ab7005b23fc4547a7fcc5d727fc09fbd3de6c953 100644 (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",
index 6979ce06775d05e67fd189a3f40e6f038ad386b3..7000c7115f30cb30dd24c888d79db2f0045f0a4c 100644 (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;