From 0a76e6570b8f6eeac146a726a82c7e0e2315cdff Mon Sep 17 00:00:00 2001
From: Thomas Munro
Date: Tue, 25 May 2021 20:42:17 +1200
Subject: [PATCH v6 1/4] Allow restricted relative tablespace paths.
Traditionally, tablespace paths provided to LOCATION had to be absolute,
because relative paths risked colliding with PostgreSQL-managed files
and generally blurring the boundary between system-managed and
user-managed data.
This commit adds a very small exception: it allows relative paths to be
used, but only under a new directory "pg_user_files". This is intended
to allow automated testing of replication in later patches.
Discussion: https://postgr.es/m/CA%2BhUKGKpRWQ9SxdxxDmTBCJoR0YnFpMBe7kyzY8SUQk%2BHeskxg%40mail.gmail.com
---
src/backend/commands/tablespace.c | 23 ++++++++++++++++++++---
src/bin/initdb/initdb.c | 1 +
src/common/string.c | 9 +++++++++
src/include/common/string.h | 1 +
4 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 4b96eec9df..adfbde6b29 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -70,6 +70,7 @@
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "common/file_perm.h"
+#include "common/string.h"
#include "miscadmin.h"
#include "postmaster/bgwriter.h"
#include "storage/fd.h"
@@ -267,14 +268,16 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
errmsg("tablespace location cannot contain single quotes")));
/*
- * Allowing relative paths seems risky
+ * Allowing relative paths seems risky in general, but we'll allow it under
+ * pg_user_files.
*
* this also helps us ensure that location is not empty or whitespace
*/
- if (!is_absolute_path(location))
+ if (!is_absolute_path(location) &&
+ !pg_str_startswith(location, "pg_user_files/"))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("tablespace location must be an absolute path")));
+ errmsg("tablespace location must be an absolute path or a relative path under pg_user_files/")));
/*
* Check that location isn't too long. Remember that we're going to append
@@ -587,6 +590,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
static void
create_tablespace_directories(const char *location, const Oid tablespaceoid)
{
+ char buffer[MAXPGPATH];
char *linkloc;
char *location_with_version_dir;
struct stat st;
@@ -651,6 +655,19 @@ create_tablespace_directories(const char *location, const Oid tablespaceoid)
if (InRecovery)
remove_tablespace_symlink(linkloc);
+#ifndef WIN32
+ /*
+ * If location is relative to pgdata, prefix it with ".." so that we have a
+ * path relative to the symlink. (Not needed for Windows, because our
+ * symlink() wrapper will interpret it as relative to pgdata instead).
+ */
+ if (!is_absolute_path(location))
+ {
+ snprintf(buffer, sizeof(buffer), "../%s", location);
+ location = buffer;
+ }
+#endif
+
/*
* Create the symlink under PGDATA
*/
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 3c61c789e4..5ce1c28174 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -224,6 +224,7 @@ static const char *const subdirs[] = {
"pg_tblspc",
"pg_stat",
"pg_stat_tmp",
+ "pg_user_files",
"pg_xact",
"pg_logical",
"pg_logical/snapshots",
diff --git a/src/common/string.c b/src/common/string.c
index 3aa378c051..7e7e34f1f5 100644
--- a/src/common/string.c
+++ b/src/common/string.c
@@ -24,6 +24,15 @@
#include "common/string.h"
+/*
+ * Returns whether the string `str' has the prefix `start'.
+ */
+bool
+pg_str_startswith(const char *str, const char *start)
+{
+ return strncmp(str, start, strlen(start)) == 0;
+}
+
/*
* Returns whether the string `str' has the postfix `end'.
*/
diff --git a/src/include/common/string.h b/src/include/common/string.h
index 8eb5271ec8..ddd384f6f9 100644
--- a/src/include/common/string.h
+++ b/src/include/common/string.h
@@ -21,6 +21,7 @@ typedef struct PromptInterruptContext
} PromptInterruptContext;
/* functions in src/common/string.c */
+extern bool pg_str_startswith(const char *str, const char *start);
extern bool pg_str_endswith(const char *str, const char *end);
extern int strtoint(const char *pg_restrict str, char **pg_restrict endptr,
int base);
--
2.33.1