diff --git a/src/include/port.h b/src/include/port.h
index 2f5abdd33486d22148952fdf4e141c2b95f3798e..7ad464c07dc9417c806bb66511573b3ea82401c8 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -284,8 +284,11 @@ 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(char *path);
 
 #define symlink(oldpath, newpath)	pgsymlink(oldpath, newpath)
+#define readlink(path, buf, size)	pgreadlink(path, buf, size)
 #endif
 
 extern bool rmtree(const char *path, bool rmtopdir);
diff --git a/src/port/dirmod.c b/src/port/dirmod.c
index 0d373a428bcc301d204c2d9fabcd46a36368d1ae..b6a5564c2c49373a39d2beebffc28836013a734c 100644
--- a/src/port/dirmod.c
+++ b/src/port/dirmod.c
@@ -297,6 +297,124 @@ pgsymlink(const char *oldpath, const char *newpath)
 
 	return 0;
 }
+
+/*
+ *	pgreadlink - uses Win32 junction points
+ */
+int
+pgreadlink(const char *path, char *buf, size_t size)
+{
+	DWORD		attr;
+	HANDLE		h;
+	char		buffer[MAX_PATH * sizeof(WCHAR) + sizeof(REPARSE_JUNCTION_DATA_BUFFER)];
+	REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
+	DWORD		len;
+	int			r;
+
+	attr = GetFileAttributes(path);
+	if (attr == INVALID_FILE_ATTRIBUTES)
+	{
+		_dosmaperr(GetLastError());
+		return -1;
+	}
+	if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	h = CreateFile(path,
+				   GENERIC_READ,
+				   FILE_SHARE_READ | FILE_SHARE_WRITE,
+				   NULL,
+				   OPEN_EXISTING,
+				   FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+				   0);
+	if (h == INVALID_HANDLE_VALUE)
+	{
+		_dosmaperr(GetLastError());
+		return -1;
+	}
+
+	if (!DeviceIoControl(h,
+						 FSCTL_GET_REPARSE_POINT,
+						 NULL,
+						 0,
+						 (LPVOID) reparseBuf,
+						 sizeof(buffer),
+						 &len,
+						 NULL))
+	{
+		LPSTR		msg;
+
+		errno = 0;
+		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+					  NULL, GetLastError(),
+					  MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+					  (LPSTR) &msg, 0, NULL);
+#ifndef FRONTEND
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not get junction for \"%s\": %s",
+						path, msg)));
+#else
+		fprintf(stderr, _("could not get junction for \"%s\": %s\n"),
+				path, msg);
+#endif
+		LocalFree(msg);
+		CloseHandle(h);
+		errno = EINVAL;
+		return -1;
+	}
+	CloseHandle(h);
+
+	/* Got it, let's get some results from this */
+	if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	r = WideCharToMultiByte(CP_ACP, 0,
+							reparseBuf->PathBuffer, -1,
+							buf,
+							size,
+							NULL, NULL);
+
+	if (r <= 0)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	/*
+	 * If the path starts with "\??\", which it will do in most (all?) cases,
+	 * strip those out.
+	 */
+	if (r > 4 && strncmp(buf, "\\??\\", 4) == 0)
+	{
+		memmove(buf, buf + 4, strlen(buf + 4) + 1);
+		r -= 4;
+	}
+	return r;
+}
+
+/*
+ * Assumes the file exists, so will return false if it doesn't
+ * (since a nonexistant file is not a junction)
+ */
+bool
+pgwin32_is_junction(char *path)
+{
+	DWORD		attr = GetFileAttributes(path);
+
+	if (attr == INVALID_FILE_ATTRIBUTES)
+	{
+		_dosmaperr(GetLastError());
+		return false;
+	}
+	return ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT);
+}
 #endif   /* defined(WIN32) && !defined(__CYGWIN__) */