From 8e0e51359c0191cf673c3ddc87a3262b13f530a4 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 16 Mar 2022 23:05:39 +1300
Subject: [PATCH] Raise errors in pgwin32_is_junction().

XXX It's not very nice to use elevel like this in frontend code
---
 src/backend/access/transam/xlog.c    |  2 +-
 src/backend/replication/basebackup.c |  4 ++--
 src/backend/storage/file/fd.c        |  2 +-
 src/backend/utils/adt/misc.c         |  2 +-
 src/bin/pg_checksums/pg_checksums.c  |  2 +-
 src/bin/pg_rewind/file_ops.c         |  2 +-
 src/common/file_utils.c              |  5 +++-
 src/include/port.h                   |  2 +-
 src/include/port/win32_port.h        |  2 +-
 src/port/dirmod.c                    | 36 +++++++++++++++++++++++++---
 10 files changed, 46 insertions(+), 13 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 4ac3871c74..819b05177d 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -8314,7 +8314,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
 			 * directories directly under pg_tblspc, which would fail below.
 			 */
 #ifdef WIN32
-			if (!pgwin32_is_junction(fullpath))
+			if (!pgwin32_is_junction(fullpath, false, ERROR))
 				continue;
 #else
 			if (get_dirent_type(fullpath, de, false, ERROR) != PGFILETYPE_LNK)
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 6884cad2c0..4be9090445 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1352,7 +1352,7 @@ sendDir(bbsink *sink, const char *path, int basepathlen, bool sizeonly,
 #ifndef WIN32
 			S_ISLNK(statbuf.st_mode)
 #else
-			pgwin32_is_junction(pathbuf)
+			pgwin32_is_junction(pathbuf, true, ERROR)
 #endif
 			)
 		{
@@ -1840,7 +1840,7 @@ convert_link_to_directory(const char *pathbuf, struct stat *statbuf)
 #ifndef WIN32
 	if (S_ISLNK(statbuf->st_mode))
 #else
-	if (pgwin32_is_junction(pathbuf))
+	if (pgwin32_is_junction(pathbuf, true, ERROR))
 #endif
 		statbuf->st_mode = S_IFDIR | pg_dir_create_mode;
 }
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 14b77f2861..ec2f49fefa 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -3453,7 +3453,7 @@ SyncDataDirectory(void)
 			xlog_is_symlink = true;
 	}
 #else
-	if (pgwin32_is_junction("pg_wal"))
+	if (pgwin32_is_junction("pg_wal", false, LOG))
 		xlog_is_symlink = true;
 #endif
 
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 89690be2ed..723504ab10 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -317,7 +317,7 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
 	 * found, a relative path to the data directory is returned.
 	 */
 #ifdef WIN32
-	if (!pgwin32_is_junction(sourcepath))
+	if (!pgwin32_is_junction(sourcepath, false, ERROR))
 		PG_RETURN_TEXT_P(cstring_to_text(sourcepath));
 #else
 	if (lstat(sourcepath, &st) < 0)
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 5f0f5ee62d..5720907d33 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -404,7 +404,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 #ifndef WIN32
 		else if (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))
 #else
-		else if (S_ISDIR(st.st_mode) || pgwin32_is_junction(fn))
+		else if (S_ISDIR(st.st_mode) || pgwin32_is_junction(fn, false, 1))
 #endif
 		{
 			/*
diff --git a/src/bin/pg_rewind/file_ops.c b/src/bin/pg_rewind/file_ops.c
index 6cb288f099..1eda2baf2f 100644
--- a/src/bin/pg_rewind/file_ops.c
+++ b/src/bin/pg_rewind/file_ops.c
@@ -434,7 +434,7 @@ recurse_dir(const char *datadir, const char *parentpath,
 #ifndef WIN32
 		else if (S_ISLNK(fst.st_mode))
 #else
-		else if (pgwin32_is_junction(fullpath))
+		else if (pgwin32_is_junction(fullpath, false, 1))
 #endif
 		{
 #if defined(HAVE_READLINK) || defined(WIN32)
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 7138068633..2db909a3aa 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -88,8 +88,11 @@ fsync_pgdata(const char *pg_data,
 		else if (S_ISLNK(st.st_mode))
 			xlog_is_symlink = true;
 	}
+#elif defined(FRONTEND)
+	if (pgwin32_is_junction(pg_wal, false, 1))
+		xlog_is_symlink = true;
 #else
-	if (pgwin32_is_junction(pg_wal))
+	if (pgwin32_is_junction(pg_wal, false, ERROR))
 		xlog_is_symlink = true;
 #endif
 
diff --git a/src/include/port.h b/src/include/port.h
index 3d103a2b31..b8118a3d51 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -284,7 +284,7 @@ extern int	pgunlink(const char *path);
 #if defined(WIN32) && !defined(__CYGWIN__)
 extern int	pgsymlink(const char *oldpath, const char *newpath);
 extern int	pgreadlink(const char *path, char *buf, size_t size);
-extern bool pgwin32_is_junction(const char *path);
+extern bool pgwin32_is_junction(const char *path, bool missing_ok, int elevel);
 
 #define symlink(oldpath, newpath)	pgsymlink(oldpath, newpath)
 #define readlink(path, buf, size)	pgreadlink(path, buf, size)
diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h
index d3cb765976..d9bec24348 100644
--- a/src/include/port/win32_port.h
+++ b/src/include/port/win32_port.h
@@ -230,7 +230,7 @@ int			setitimer(int which, const struct itimerval *value, struct itimerval *oval
  */
 extern int	pgsymlink(const char *oldpath, const char *newpath);
 extern int	pgreadlink(const char *path, char *buf, size_t size);
-extern bool pgwin32_is_junction(const char *path);
+extern bool pgwin32_is_junction(const char *path, bool missing_ok, int elevel);
 
 #define symlink(oldpath, newpath)	pgsymlink(oldpath, newpath)
 #define readlink(path, buf, size)	pgreadlink(path, buf, size)
diff --git a/src/port/dirmod.c b/src/port/dirmod.c
index 7ce042e75d..127cb6a576 100644
--- a/src/port/dirmod.c
+++ b/src/port/dirmod.c
@@ -337,19 +337,49 @@ pgreadlink(const char *path, char *buf, size_t size)
 }
 
 /*
- * Assumes the file exists, so will return false if it doesn't
- * (since a nonexistent file is not a junction)
+ * In frontend code, elevel must be 0 or 1, where 1 means write an error and
+ * exit(1); in backend code it should be 0 or a level from elog.h, to pass to
+ * ereport().  If elevel is 0, or missing_ok is set and the path doesn't exist,
+ * no logging is done.  The caller should check errno on false return, unless
+ * elevel is set to a level that doesn't return on error.
  */
 bool
-pgwin32_is_junction(const char *path)
+pgwin32_is_junction(const char *path, bool missing_ok, int elevel)
 {
 	DWORD		attr = GetFileAttributes(path);
 
+#ifdef FRONTEND
+	/*
+	 * We can't use ereport, and libport can't use the logging from libcommon
+	 * due to library order, so the options for logging are limited.
+	 */
+	Assert(elevel == 0 || elevel == 1);
+#endif
+
 	if (attr == INVALID_FILE_ATTRIBUTES)
 	{
 		_dosmaperr(GetLastError());
+
+		if (errno == ENOENT && missing_ok)
+			return false;
+
+		if (elevel != 0)
+		{
+#ifndef FRONTEND
+			ereport(elevel,
+					(errcode_for_file_access(),
+					 errmsg("could not get attributes for file \"%s\": %m",
+							path)));
+#else
+			int save_errno = errno;
+			fprintf(stderr, _("could not get attributes for file \"%s\": %s\n"),
+					path, strerror(save_errno));
+			exit(1);
+#endif
+		}
 		return false;
 	}
+	errno = 0;
 	return ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT);
 }
 #endif							/* defined(WIN32) && !defined(__CYGWIN__) */
-- 
2.35.1

