mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	pg_controldata: Prevent division-by-zero errors
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 <bossartn@amazon.com>, tests by me
This commit is contained in:
		@@ -95,7 +95,6 @@ main(int argc, char *argv[])
 | 
				
			|||||||
	char		mock_auth_nonce_str[MOCK_AUTH_NONCE_LEN * 2 + 1];
 | 
						char		mock_auth_nonce_str[MOCK_AUTH_NONCE_LEN * 2 + 1];
 | 
				
			||||||
	const char *strftime_fmt = "%c";
 | 
						const char *strftime_fmt = "%c";
 | 
				
			||||||
	const char *progname;
 | 
						const char *progname;
 | 
				
			||||||
	XLogSegNo	segno;
 | 
					 | 
				
			||||||
	char		xlogfilename[MAXFNAMELEN];
 | 
						char		xlogfilename[MAXFNAMELEN];
 | 
				
			||||||
	int			c;
 | 
						int			c;
 | 
				
			||||||
	int			i;
 | 
						int			i;
 | 
				
			||||||
@@ -169,9 +168,10 @@ main(int argc, char *argv[])
 | 
				
			|||||||
	WalSegSz = ControlFile->xlog_seg_size;
 | 
						WalSegSz = ControlFile->xlog_seg_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!IsValidWalSegSize(WalSegSz))
 | 
						if (!IsValidWalSegSize(WalSegSz))
 | 
				
			||||||
		fprintf(stderr,
 | 
							printf(_("WARNING: invalid WAL segment size\n"
 | 
				
			||||||
				_("WARNING: WAL segment size specified, %d bytes, is not a power of two between 1MB and 1GB.\n"
 | 
									 "The WAL segment size stored in the file, %d bytes, is not a power of two\n"
 | 
				
			||||||
				  "The file is corrupt and the results below are untrustworthy.\n"),
 | 
									 "between 1 MB and 1 GB.  The file is corrupt and the results below are\n"
 | 
				
			||||||
 | 
									 "untrustworthy.\n\n"),
 | 
				
			||||||
			   WalSegSz);
 | 
								   WalSegSz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
@@ -193,10 +193,20 @@ main(int argc, char *argv[])
 | 
				
			|||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Calculate name of the WAL file containing the latest checkpoint's REDO
 | 
						 * Calculate name of the WAL file containing the latest checkpoint's REDO
 | 
				
			||||||
	 * start point.
 | 
						 * 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.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
 | 
						if (WalSegSz != 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							XLogSegNo	segno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		XLByteToSeg(ControlFile->checkPointCopy.redo, segno, WalSegSz);
 | 
							XLByteToSeg(ControlFile->checkPointCopy.redo, segno, WalSegSz);
 | 
				
			||||||
		XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID,
 | 
							XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID,
 | 
				
			||||||
					 segno, WalSegSz);
 | 
										 segno, WalSegSz);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							strcpy(xlogfilename, _("???"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Format system_identifier and mock_authentication_nonce separately to
 | 
						 * Format system_identifier and mock_authentication_nonce separately to
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ use strict;
 | 
				
			|||||||
use warnings;
 | 
					use warnings;
 | 
				
			||||||
use PostgresNode;
 | 
					use PostgresNode;
 | 
				
			||||||
use TestLib;
 | 
					use TestLib;
 | 
				
			||||||
use Test::More tests => 13;
 | 
					use Test::More tests => 17;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
program_help_ok('pg_controldata');
 | 
					program_help_ok('pg_controldata');
 | 
				
			||||||
program_version_ok('pg_controldata');
 | 
					program_version_ok('pg_controldata');
 | 
				
			||||||
@@ -16,3 +16,22 @@ $node->init;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
command_like([ 'pg_controldata', $node->data_dir ],
 | 
					command_like([ 'pg_controldata', $node->data_dir ],
 | 
				
			||||||
	qr/checkpoint/, 'pg_controldata produces output');
 | 
						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');
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user