Skip foreign tablespaces when running pg_checksums/pg_verify_checksums
authorMichael Paquier <michael@paquier.xyz>
Thu, 27 Feb 2020 06:31:27 +0000 (15:31 +0900)
committerMichael Paquier <michael@paquier.xyz>
Thu, 27 Feb 2020 06:31:27 +0000 (15:31 +0900)
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
src/bin/pg_checksums/t/002_actions.pl

index 9bd0bf947f7fa77ebb7a585c4a459b2f1fb194ad..eb179500960691fd1b89c74264d151bfbe5a2686 100644 (file)
@@ -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;
index 83a730ea94784d85a5616ad61f6acee222f4382d..4e4934532a30d27520d7d72ecbedaa03f64ff2ec 100644 (file)
@@ -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.