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