From f44c286ec129deaf120986a5025996001cfd8ffe Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 29 Jun 2015 15:09:34 +0900 Subject: [PATCH 5/5] Fix symlink usage in pg_rewind Make use of pg_readlink to determine where a symlink in a source PGDATA streamed is from. Also remove restrictions related to symlink comparisons on source and target instances, those need to be only limited to tablespaces. --- src/bin/pg_rewind/file_ops.c | 2 ++ src/bin/pg_rewind/filemap.c | 11 +++-------- src/bin/pg_rewind/filemap.h | 1 + src/bin/pg_rewind/libpq_fetch.c | 32 ++++++++++++++++---------------- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/bin/pg_rewind/file_ops.c b/src/bin/pg_rewind/file_ops.c index c2d8aa1..29ddb1d 100644 --- a/src/bin/pg_rewind/file_ops.c +++ b/src/bin/pg_rewind/file_ops.c @@ -139,6 +139,7 @@ remove_target(file_entry_t *entry) remove_target_file(entry->path); break; + case FILE_TYPE_TABLESPACE: case FILE_TYPE_SYMLINK: remove_target_symlink(entry->path); break; @@ -156,6 +157,7 @@ create_target(file_entry_t *entry) create_target_dir(entry->path); break; + case FILE_TYPE_TABLESPACE: case FILE_TYPE_SYMLINK: create_target_symlink(entry->path, entry->link_target); break; diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c index 05eff68..6ef2342 100644 --- a/src/bin/pg_rewind/filemap.c +++ b/src/bin/pg_rewind/filemap.c @@ -112,12 +112,6 @@ process_source_file(const char *path, file_type_t type, size_t newsize, switch (type) { case FILE_TYPE_DIRECTORY: - if (exists && !S_ISDIR(statbuf.st_mode)) - { - /* it's a directory in source, but not in target. Strange.. */ - pg_fatal("\"%s\" is not a directory\n", localpath); - } - if (!exists) action = FILE_ACTION_CREATE; else @@ -125,7 +119,7 @@ process_source_file(const char *path, file_type_t type, size_t newsize, oldsize = 0; break; - case FILE_TYPE_SYMLINK: + case FILE_TYPE_TABLESPACE: if (exists && #ifndef WIN32 !S_ISLNK(statbuf.st_mode) @@ -140,7 +134,8 @@ process_source_file(const char *path, file_type_t type, size_t newsize, */ pg_fatal("\"%s\" is not a symbolic link\n", localpath); } - + /* fallback to default */ + case FILE_TYPE_SYMLINK: if (!exists) action = FILE_ACTION_CREATE; else diff --git a/src/bin/pg_rewind/filemap.h b/src/bin/pg_rewind/filemap.h index 9943ec5..35540d6 100644 --- a/src/bin/pg_rewind/filemap.h +++ b/src/bin/pg_rewind/filemap.h @@ -36,6 +36,7 @@ typedef enum { FILE_TYPE_REGULAR, FILE_TYPE_DIRECTORY, + FILE_TYPE_TABLESPACE, FILE_TYPE_SYMLINK } file_type_t; diff --git a/src/bin/pg_rewind/libpq_fetch.c b/src/bin/pg_rewind/libpq_fetch.c index 05aa133..d77c931 100644 --- a/src/bin/pg_rewind/libpq_fetch.c +++ b/src/bin/pg_rewind/libpq_fetch.c @@ -140,30 +140,26 @@ libpqProcessFileList(void) * Create a recursive directory listing of the whole data directory. * * The WITH RECURSIVE part does most of the work. The second part gets the - * targets of the symlinks in pg_tblspc directory. - * - * XXX: There is no backend function to get a symbolic link's target in - * general, so if the admin has put any custom symbolic links in the data - * directory, they won't be copied correctly. + * targets of the symlinks in the data directory. */ sql = - "WITH RECURSIVE files (path, filename, size, isdir) AS (\n" - " SELECT '' AS path, filename, size, isdir FROM\n" + "WITH RECURSIVE files (path, filename, size, isdir, islink) AS (\n" + " SELECT '' AS path, filename, size, isdir, islink FROM\n" " (SELECT pg_ls_dir('.', true, false) AS filename) AS fn,\n" " pg_stat_file(fn.filename, true) AS this\n" " UNION ALL\n" " SELECT parent.path || parent.filename || '/' AS path,\n" - " fn, this.size, this.isdir\n" + " fn, this.size, this.isdir, this.islink\n" " FROM files AS parent,\n" " pg_ls_dir(parent.path || parent.filename, true, false) AS fn,\n" " pg_stat_file(parent.path || parent.filename || '/' || fn, true) AS this\n" " WHERE parent.isdir = 't'\n" ")\n" - "SELECT path || filename, size, isdir,\n" - " pg_tablespace_location(pg_tablespace.oid) AS link_target\n" - "FROM files\n" - "LEFT OUTER JOIN pg_tablespace ON files.path = 'pg_tblspc/'\n" - " AND oid::text = files.filename\n"; + "SELECT path || filename, size, isdir, islink,\n" + " path = 'pg_tblspc/' AS istblspc,\n" + " CASE WHEN islink THEN pg_readlink(path || filename)\n" + " ELSE NULL END AS link_target\n" + "FROM files\n"; res = PQexec(conn, sql); if (PQresultStatus(res) != PGRES_TUPLES_OK) @@ -171,7 +167,7 @@ libpqProcessFileList(void) PQresultErrorMessage(res)); /* sanity check the result set */ - if (PQnfields(res) != 4) + if (PQnfields(res) != 6) pg_fatal("unexpected result set while fetching file list\n"); /* Read result to local variables */ @@ -180,7 +176,9 @@ libpqProcessFileList(void) char *path = PQgetvalue(res, i, 0); int filesize = atoi(PQgetvalue(res, i, 1)); bool isdir = (strcmp(PQgetvalue(res, i, 2), "t") == 0); - char *link_target = PQgetvalue(res, i, 3); + bool islink = (strcmp(PQgetvalue(res, i, 3), "t") == 0); + bool istblspc = (strcmp(PQgetvalue(res, i, 4), "t") == 0); + char *link_target = PQgetvalue(res, i, 5); file_type_t type; if (PQgetisnull(res, 0, 1)) @@ -192,7 +190,9 @@ libpqProcessFileList(void) continue; } - if (link_target[0]) + if (istblspc) + type = FILE_TYPE_TABLESPACE; + else if (islink) type = FILE_TYPE_SYMLINK; else if (isdir) type = FILE_TYPE_DIRECTORY; -- 2.4.5