Skip to content

Commit

Permalink
Fix scanning of tablespaces.
Browse files Browse the repository at this point in the history
Commit 60fae02 introduced a whitelist of relation filenames to compare files or
directories to. However, it missed that non-default tablespaces have a
PG_<VER>_<CATNO> directory, that was not considered a relation and skipped, so
pg_checksums would not verify or activate checksums in tablespaces.

Fix this by adopting a proposed patch by Michael Paquier that only checks
proper files for being a relation file, not directories. Also adapt TAP tests
inducing corruption in a table in a non-default tablespace.
  • Loading branch information
mbanck committed Nov 27, 2018
1 parent e2d9c3b commit a444a31
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 15 deletions.
26 changes: 24 additions & 2 deletions pg_checksums.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ extern char *optarg;
#define INT64_MODIFIER "l"
#endif

#define PG_TEMP_FILES_DIR "pgsql_tmp"
#define PG_TEMP_FILE_PREFIX "pgsql_tmp"

static int64 files = 0;
static int64 blocks = 0;
static int64 skippedblocks = 0;
Expand Down Expand Up @@ -114,7 +117,6 @@ usage(void)
printf(_("Report bugs to https://github.com/credativ/pg_checksums/issues/new.\n"));
}


/*
* isRelFileName
*
Expand Down Expand Up @@ -464,7 +466,20 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
char fn[MAXPGPATH];
struct stat st;

if (!isRelFileName(de->d_name))
if (strcmp(de->d_name, ".") == 0 ||
strcmp(de->d_name, "..") == 0)
continue;

/* Skip temporary files */
if (strncmp(de->d_name,
PG_TEMP_FILE_PREFIX,
strlen(PG_TEMP_FILE_PREFIX)) == 0)
continue;

/* Skip temporary folders */
if (strncmp(de->d_name,
PG_TEMP_FILES_DIR,
strlen(PG_TEMP_FILES_DIR)) == 0)
continue;

snprintf(fn, sizeof(fn), "%s/%s", path, de->d_name);
Expand All @@ -490,6 +505,13 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
*segmentpath;
BlockNumber segmentno = 0;

/*
* Only normal relation files can be analyzed. Note that this
* skips temporary relations.
*/
if (!isRelFileName(de->d_name))
continue;

/*
* Cut off at the segment boundary (".") to get the segment number
* in order to mix it into the checksum. Then also cut off at the
Expand Down
65 changes: 52 additions & 13 deletions t/001_checksums.pl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Config;
use PostgresNode;
use TestLib;
use Test::More tests => 44;
use Test::More tests => 49;

program_help_ok('pg_checksums');
program_version_ok('pg_checksums');
Expand Down Expand Up @@ -80,34 +80,73 @@
$node->command_ok(['pg_checksums', '-a', '-D', $pgdata],
'pg_checksums are again activated in offline cluster');

#exit 0;
$node->start;

$node->command_ok(['pg_checksums', '-c', '-D', $pgdata],
'pg_checksums can be verified in online cluster');

# create table to corrupt and get their relfilenode
my $file_corrupt = $node->safe_psql('postgres',
q{SELECT a INTO corrupt1 FROM generate_series(1,10000) AS a; ALTER TABLE corrupt1 SET (autovacuum_enabled=false); SELECT pg_relation_filepath('corrupt1')}
create_corruption($node, 'corrupt1', 'pg_default');

$node->command_checks_all([ 'pg_checksums', '-c', '-D', $pgdata],
1,
[qr/Bad checksums: 1/s],
[qr/checksum verification failed/s],
'pg_checksums reports checksum mismatch'
);

# set page header and block sizes
my $pageheader_size = 24;
my $block_size = $node->safe_psql('postgres', 'SHOW block_size;');
# drop corrupt table again and make sure there is no more corruption
$node->safe_psql('postgres', 'DROP TABLE corrupt1;');
$node->command_ok(['pg_checksums', '-c', '-D', $pgdata],
'pg_checksums can be verified in online cluster: '.getcwd());

# induce corruption
$node->stop;
open my $file, '+<', "$pgdata/$file_corrupt";
seek($file, $pageheader_size, 0);
syswrite($file, '\0\0\0\0\0\0\0\0\0');
close $file;

# create table to corrupt in a non-default tablespace and get their relfilenode
my $tablespace_dir = getcwd()."/tmp_check/ts_corrupt_dir";
mkdir ($tablespace_dir);
$node->safe_psql('postgres', "CREATE TABLESPACE ts_corrupt LOCATION '".$tablespace_dir."';");
create_corruption($node, 'corrupt2', 'ts_corrupt');

$node->command_checks_all([ 'pg_checksums', '-c', '-D', $pgdata],
1,
[qr/Bad checksums: 1/s],
[qr/checksum verification failed/s],
'pg_checksums reports checksum mismatch'
'pg_checksums reports checksum mismatch on non-default tablespace'
);

# drop corrupt table again and make sure there is no more corruption
$node->safe_psql('postgres', 'DROP TABLE corrupt2;');
$node->command_ok(['pg_checksums', '-c', '-D', $pgdata],
'pg_checksums can be verified in online cluster');

# Utility routine to create a table with corrupted checksums.
# It stops the node (if running), and starts it again.
sub create_corruption
{
my $node = shift;
my $table = shift;
my $tablespace = shift;

my $query = "SELECT a INTO ".$table." FROM generate_series(1,10000) AS a; ALTER TABLE ".$table." SET (autovacuum_enabled=false), SET TABLESPACE ".$tablespace."; SELECT pg_relation_filepath('".$table."')";
my $file_name = $node->safe_psql('postgres', $query);

# set page header and block sizes
my $pageheader_size = 24;
my $block_size = $node->safe_psql('postgres', 'SHOW block_size;');

$node->stop;

open my $file, '+<', "$pgdata/$file_name";
seek($file, $pageheader_size, 0);
syswrite($file, '\0\0\0\0\0\0\0\0\0');
close $file;

$node->start;

return;
}

# Utility routine to check that pg_checksums is able to detect
# correctly-named relation files filled with some corrupted data.
sub fail_corrupt
Expand Down

0 comments on commit a444a31

Please sign in to comment.