pg_controldata: Prevent division-by-zero errors
authorPeter Eisentraut <[email protected]>
Wed, 21 Mar 2018 16:14:53 +0000 (12:14 -0400)
committerPeter Eisentraut <[email protected]>
Wed, 21 Mar 2018 16:21:23 +0000 (12:21 -0400)
If the control file is corrupted and specifies the WAL segment size
to be 0 bytes, calculating the latest checkpoint's REDO WAL file
will fail with a division-by-zero error.  Show it as "???" instead.

Also reword the warning message a bit and send it to stdout, like the
other pre-existing warning messages.

Add some tests for dealing with a corrupted pg_control file.

Author: Nathan Bossart <[email protected]>, tests by me

src/bin/pg_controldata/pg_controldata.c
src/bin/pg_controldata/t/001_pg_controldata.pl

index cc73b7d6c29f609393c46dad522634f5fedff722..f9dc854b4a95b527618fa2dd25d2040b12ba00b4 100644 (file)
@@ -95,7 +95,6 @@ main(int argc, char *argv[])
        char            mock_auth_nonce_str[MOCK_AUTH_NONCE_LEN * 2 + 1];
        const char *strftime_fmt = "%c";
        const char *progname;
-       XLogSegNo       segno;
        char            xlogfilename[MAXFNAMELEN];
        int                     c;
        int                     i;
@@ -169,10 +168,11 @@ main(int argc, char *argv[])
        WalSegSz = ControlFile->xlog_seg_size;
 
        if (!IsValidWalSegSize(WalSegSz))
-               fprintf(stderr,
-                               _("WARNING: WAL segment size specified, %d bytes, is not a power of two between 1MB and 1GB.\n"
-                                 "The file is corrupt and the results below are untrustworthy.\n"),
-                               WalSegSz);
+               printf(_("WARNING: invalid WAL segment size\n"
+                                "The WAL segment size stored in the file, %d bytes, is not a power of two\n"
+                                "between 1 MB and 1 GB.  The file is corrupt and the results below are\n"
+                                "untrustworthy.\n\n"),
+                          WalSegSz);
 
        /*
         * This slightly-chintzy coding will work as long as the control file
@@ -193,10 +193,20 @@ main(int argc, char *argv[])
        /*
         * Calculate name of the WAL file containing the latest checkpoint's REDO
         * start point.
+        *
+        * A corrupted control file could report a WAL segment size of 0, and to
+        * guard against division by zero, we need to treat that specially.
         */
-       XLByteToSeg(ControlFile->checkPointCopy.redo, segno, WalSegSz);
-       XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID,
-                                segno, WalSegSz);
+       if (WalSegSz != 0)
+       {
+               XLogSegNo       segno;
+
+               XLByteToSeg(ControlFile->checkPointCopy.redo, segno, WalSegSz);
+               XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID,
+                                        segno, WalSegSz);
+       }
+       else
+               strcpy(xlogfilename, _("???"));
 
        /*
         * Format system_identifier and mock_authentication_nonce separately to
index 40405516cad3eb3e7c21d5ca1129fd0332343d07..af9fad7a38b8ce2a1e800c55510c246a289efa78 100644 (file)
@@ -2,7 +2,7 @@ use strict;
 use warnings;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 13;
+use Test::More tests => 17;
 
 program_help_ok('pg_controldata');
 program_version_ok('pg_controldata');
@@ -16,3 +16,22 @@ $node->init;
 
 command_like([ 'pg_controldata', $node->data_dir ],
        qr/checkpoint/, 'pg_controldata produces output');
+
+
+# check with a corrupted pg_control
+
+my $pg_control = $node->data_dir . '/global/pg_control';
+my $size = (stat($pg_control))[7];
+
+open my $fh, '>', $pg_control or BAIL_OUT($!);
+binmode $fh;
+# fill file with zeros
+print $fh pack("x[$size]");
+close $fh;
+
+command_checks_all([ 'pg_controldata', $node->data_dir ],
+                                  0,
+                                  [ qr/WARNING: Calculated CRC checksum does not match value stored in file/,
+                                        qr/WARNING: invalid WAL segment size/ ],
+                                  [ qr/^$/ ],
+                                  'pg_controldata with corrupted pg_control');