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

index a33ac6924f89e7d4c2b777bd8ac066037ffbdb6c..ba11e8ddf46288da36696fcbdafe6183b492453d 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "catalog/pg_control.h"
 #include "common/controldata_utils.h"
+#include "common/relpath.h"
 #include "getopt_long.h"
 #include "pg_getopt.h"
 #include "storage/bufpage.h"
@@ -238,7 +239,51 @@ scan_directory(const char *basedir, const char *subdir)
 #else
        else if (S_ISDIR(st.st_mode) || pgwin32_is_junction(fn))
 #endif
-           scan_directory(path, de->d_name);
+       {
+           /*
+            * 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)
+               {
+                   fprintf(stderr, _("%s: could not stat file \"%s\": %s\n"),
+                           progname, tblspc_path, strerror(errno));
+                   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 */
+               scan_directory(tblspc_path,
+                              TABLESPACE_VERSION_DIRECTORY);
+           }
+           else
+           {
+               scan_directory(path, de->d_name);
+           }
+       }
    }
    closedir(dir);
 }