From f6e8e8b3809b58a0ab6c5369e8baa1fd9b96e73e Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Thu, 27 Feb 2020 15:31:48 +0900 Subject: [PATCH] Skip foreign tablespaces when running pg_checksums/pg_verify_checksums Attempting to use pg_checksums (pg_verify_checksums in 11) on a data folder which includes tablespace paths used across multiple major versions would cause pg_checksums to scan all directories present in pg_tblspc, and not only marked with TABLESPACE_VERSION_DIRECTORY. This could lead to failures when for example running sanity checks on an upgraded instance with --check. Even worse, it was possible to rewrite on-disk pages with --enable for a cluster potentially online. This commit makes pg_checksums skip any directories not named TABLESPACE_VERSION_DIRECTORY, similarly to what is done for base backups. Reported-by: Michael Banck Author: Michael Banck, Bernd Helmle Discussion: https://postgr.es/m/62031974fd8e941dd8351fbc8c7eff60d59c5338.camel@credativ.de backpatch-through: 11 --- src/bin/pg_checksums/pg_checksums.c | 47 ++++++++++++++++++++++++++- src/bin/pg_checksums/t/002_actions.pl | 9 ++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c index e21b684e9d..03c3da3d73 100644 --- a/src/bin/pg_checksums/pg_checksums.c +++ b/src/bin/pg_checksums/pg_checksums.c @@ -385,7 +385,52 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly) #else else if (S_ISDIR(st.st_mode) || pgwin32_is_junction(fn)) #endif - dirsize += scan_directory(path, de->d_name, sizeonly); + { + /* + * If going through the entries of pg_tblspc, we assume to operate + * on tablespace locations where only TABLESPACE_VERSION_DIRECTORY + * is valid, resolving the linked locations and dive into them + * directly. + */ + if (strncmp("pg_tblspc", subdir, strlen("pg_tblspc")) == 0) + { + char tblspc_path[MAXPGPATH]; + struct stat tblspc_st; + + /* + * Resolve tablespace location path and check whether + * TABLESPACE_VERSION_DIRECTORY exists. Not finding a valid + * location is unexpected, since there should be no orphaned + * links and no links pointing to something else than a + * directory. + */ + snprintf(tblspc_path, sizeof(tblspc_path), "%s/%s/%s", + path, de->d_name, TABLESPACE_VERSION_DIRECTORY); + + if (lstat(tblspc_path, &tblspc_st) < 0) + { + pg_log_error("could not stat file \"%s\": %m", + tblspc_path); + exit(1); + } + + /* + * Move backwards once as the scan needs to happen for the + * contents of TABLESPACE_VERSION_DIRECTORY. + */ + snprintf(tblspc_path, sizeof(tblspc_path), "%s/%s", + path, de->d_name); + + /* Looks like a valid tablespace location */ + dirsize += scan_directory(tblspc_path, + TABLESPACE_VERSION_DIRECTORY, + sizeonly); + } + else + { + dirsize += scan_directory(path, de->d_name, sizeonly); + } + } } closedir(dir); return dirsize; diff --git a/src/bin/pg_checksums/t/002_actions.pl b/src/bin/pg_checksums/t/002_actions.pl index 83a730ea94..4e4934532a 100644 --- a/src/bin/pg_checksums/t/002_actions.pl +++ b/src/bin/pg_checksums/t/002_actions.pl @@ -5,7 +5,7 @@ use strict; use warnings; use PostgresNode; use TestLib; -use Test::More tests => 62; +use Test::More tests => 63; # Utility routine to create and check a table with corrupted checksums @@ -217,6 +217,13 @@ sub fail_corrupt # Stop instance for the follow-up checks. $node->stop; +# Create a fake tablespace location that should not be scanned +# when verifying checksums. +mkdir "$tablespace_dir/PG_99_999999991/"; +append_to_file "$tablespace_dir/PG_99_999999991/foo", "123"; +command_ok([ 'pg_checksums', '--check', '-D', $pgdata ], + "succeeds with foreign tablespace"); + # Authorized relation files filled with corrupted data cause the # checksum checks to fail. Make sure to use file names different # than the previous ones.