From 80fb66c1feb783ee6366aa3e1315395205060021 Mon Sep 17 00:00:00 2001
From: Juan Jose Santamaria Flecha <juanjo.santamaria@gmail.com>
Date: Mon, 24 Jun 2019 12:56:13 -0400
Subject: [PATCH] WIP support for large files on Win32 . Workaround for Windows
 <sys/stat.h> shortcomings, reimplement fstat()/stat() on top of
 GetFileAttributesEx() and use 64 bits struct stat.

---
 configure                     |  11 ++++
 src/include/port/win32_port.h |  62 +++++++++++++++---
 src/port/dirmod.c             |  52 ---------------
 src/port/stat_pg_fixed.c      | 149 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 214 insertions(+), 60 deletions(-)
 create mode 100644 src/port/stat_pg_fixed.c

diff --git a/configure b/configure
index 8d47071..e8dcfcb 100755
--- a/configure
+++ b/configure
@@ -16204,6 +16204,12 @@ fi
 esac
 
   case " $LIBOBJS " in
+  *" stat_pg_fixed.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS stat_pg_fixed.$ac_objext"
+ ;;
+esac
+
+  case " $LIBOBJS " in
   *" kill.$ac_objext "* ) ;;
   *) LIBOBJS="$LIBOBJS kill.$ac_objext"
  ;;
@@ -16282,6 +16288,11 @@ if test "$PORTNAME" = "cygwin"; then
  ;;
 esac
 
+  case " $LIBOBJS " in
+  *" stat_pg_fixed.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS stat_pg_fixed.$ac_objext"
+ ;;
+esac
 fi
 
 ac_fn_c_check_func "$LINENO" "syslog" "ac_cv_func_syslog"
diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h
index f4841fb..fd1620b 100644
--- a/src/include/port/win32_port.h
+++ b/src/include/port/win32_port.h
@@ -52,7 +52,12 @@
 #include <direct.h>
 #include <sys/utime.h>			/* for non-unicode version */
 #undef near
-#include <sys/stat.h>			/* needed before sys/stat hacking below */
+/* needed before sys/stat hacking below */
+#define fstat microsoft_native_fstat
+#define stat microsoft_native_stat
+#include <sys/stat.h>
+#undef fstat
+#undef stat
 
 /* Must be here to avoid conflicting with prototype in windows.h */
 #define mkdir(a,b)	mkdir(a)
@@ -254,15 +259,56 @@ typedef int pid_t;
 
 /*
  * stat() is not guaranteed to set the st_size field on win32, so we
- * redefine it to our own implementation that is.
+ * redefine it to our own implementation. See src/port/stat_pg_fixed.c
  *
- * Some frontends don't need the size from stat, so if UNSAFE_STAT_OK
- * is defined we don't bother with this.
+ * The struct stat is 32 bit in MSVC so we redefine it as a copy of struct
+ * _stat64. This also fixes the struct size across Cygwin and MINGW builds.
  */
-#ifndef UNSAFE_STAT_OK
-extern int	pgwin32_safestat(const char *path, struct stat *buf);
-#define stat(a,b) pgwin32_safestat(a,b)
-#endif
+struct stat                     /* It is actually _stat64 */
+{
+	_dev_t         st_dev;
+	_ino_t         st_ino;
+	unsigned short st_mode;
+	short          st_nlink;
+	short          st_uid;
+	short          st_gid;
+	_dev_t         st_rdev;
+	__int64        st_size;
+	__time64_t     st_atime;
+	__time64_t     st_mtime;
+	__time64_t     st_ctime;
+};
+extern int _pgfstat64(int fileno, struct stat *buf);
+extern int _pgstat64(char const *name, struct stat *buf);
+#if defined(_MSC_VER)
+static __inline int __CRTDECL
+fstat(int _Filenumber, struct stat* _Stat)
+{
+	_STATIC_ASSERT(sizeof(struct stat) == sizeof(struct __stat64));
+	return _pgfstat64(_Filenumber, _Stat);
+}
+
+static __inline int __CRTDECL
+stat(char* const _Filename, struct stat* _Stat)
+{
+	_STATIC_ASSERT(sizeof(struct stat) == sizeof(struct __stat64));
+	return _pgstat64(_Filename, _Stat);
+}
+#else /* defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__) */
+static __inline__ int
+fstat(int _Filenumber, struct stat* _Stat)
+{
+	Assert(sizeof(struct stat) == sizeof(struct __stat64));
+	return _pgfstat64(_Filenumber, _Stat);
+}
+
+static __inline__ int
+stat(char* const _Filename, struct stat* _Stat)
+{
+	Assert(sizeof(struct stat) == sizeof(struct __stat64));
+	return _pgstat64(_Filename, _Stat);
+}
+#endif /* defined(_MSC_VER) */
 
 /* These macros are not provided by older MinGW, nor by MSVC */
 #ifndef S_IRUSR
diff --git a/src/port/dirmod.c b/src/port/dirmod.c
index d793240..ec4b47f 100644
--- a/src/port/dirmod.c
+++ b/src/port/dirmod.c
@@ -353,55 +353,3 @@ pgwin32_is_junction(const char *path)
 	return ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT);
 }
 #endif							/* defined(WIN32) && !defined(__CYGWIN__) */
-
-
-#if defined(WIN32) && !defined(__CYGWIN__)
-
-#undef stat
-
-/*
- * The stat() function in win32 is not guaranteed to update the st_size
- * field when run. So we define our own version that uses the Win32 API
- * to update this field.
- */
-int
-pgwin32_safestat(const char *path, struct stat *buf)
-{
-	int			r;
-	WIN32_FILE_ATTRIBUTE_DATA attr;
-
-	r = stat(path, buf);
-	if (r < 0)
-	{
-		if (GetLastError() == ERROR_DELETE_PENDING)
-		{
-			/*
-			 * File has been deleted, but is not gone from the filesystem yet.
-			 * This can happen when some process with FILE_SHARE_DELETE has it
-			 * open and it will be fully removed once that handle is closed.
-			 * Meanwhile, we can't open it, so indicate that the file just
-			 * doesn't exist.
-			 */
-			errno = ENOENT;
-			return -1;
-		}
-
-		return r;
-	}
-
-	if (!GetFileAttributesEx(path, GetFileExInfoStandard, &attr))
-	{
-		_dosmaperr(GetLastError());
-		return -1;
-	}
-
-	/*
-	 * XXX no support for large files here, but we don't do that in general on
-	 * Win32 yet.
-	 */
-	buf->st_size = attr.nFileSizeLow;
-
-	return 0;
-}
-
-#endif
diff --git a/src/port/stat_pg_fixed.c b/src/port/stat_pg_fixed.c
new file mode 100644
index 0000000..98de7c4
--- /dev/null
+++ b/src/port/stat_pg_fixed.c
@@ -0,0 +1,149 @@
+/*-------------------------------------------------------------------------
+ *
+ * stat_pg_fixed.c
+ *	  Replacements for <sys/stat.h> functions using GetFileAttributesEx on
+ *    Win32.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/port/stat_pg_fixed.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef WIN32
+
+#include "c.h"
+#include <tchar.h>
+#if defined(__CYGWIN__)
+#include <w32api/windows.h>
+#else
+#include <windows.h>
+#endif
+
+static const unsigned __int64 EpochShift = UINT64CONST(116444736000000000);
+
+/* Converts FILETIME to time_t */
+static __time64_t
+cvt_ft2ut(FILETIME const * ft)
+{
+	ULARGE_INTEGER unified_ft;
+
+	unified_ft.LowPart  = ft->dwLowDateTime;
+	unified_ft.HighPart = ft->dwHighDateTime;
+
+	if (unified_ft.QuadPart < EpochShift)
+		return -1;
+
+	unified_ft.QuadPart -= EpochShift;
+	unified_ft.QuadPart /= 10 * 1000 * 1000;
+
+	return ((__int64) unified_ft.HighPart) << 32 |
+		   (__int64) unified_ft.LowPart;
+}
+
+static unsigned short
+cvt_attr2uxmode(int attr, const _TCHAR * name)
+{
+	unsigned short uxmode;
+	const _TCHAR * p;
+
+	uxmode = (unsigned short)((attr & FILE_ATTRIBUTE_DIRECTORY) ?
+			 (_S_IFDIR | _S_IEXEC) : (_S_IFREG));
+
+	uxmode |= (unsigned short)(attr & FILE_ATTRIBUTE_READONLY) ?
+			  (_S_IREAD) : (_S_IREAD | _S_IWRITE);
+
+	if (p = _tcsrchr(name, _T('.')))
+	{
+		if (! _tcsicmp(p, _T(".exe")) ||
+			! _tcsicmp(p, _T(".cmd")) ||
+			! _tcsicmp(p, _T(".bat")) ||
+			! _tcsicmp(p, _T(".com")))
+			uxmode |= _S_IEXEC;
+	}
+
+	uxmode |= (uxmode & 0700) >> 3;
+	uxmode |= (uxmode & 0700) >> 6;
+
+	return uxmode;
+}
+
+int
+_pgstat64(char const *name, struct stat *buf)
+{
+	WIN32_FILE_ATTRIBUTE_DATA faData;
+
+	if (NULL == name || NULL == buf)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!GetFileAttributesEx(name, GetFileExInfoStandard, & faData))
+	{
+		DWORD		error = GetLastError();
+		if (error== ERROR_DELETE_PENDING)
+		{
+			/*
+			 * File has been deleted, but is not gone from the filesystem yet.
+			 * This can happen when some process with FILE_SHARE_DELETE has it
+			 * open and it will be fully removed once that handle is closed.
+			 * Meanwhile, we can't open it, so indicate that the file just
+			 * doesn't exist.
+			 */
+			errno = ENOENT;
+		}
+		_dosmaperr(error);
+		return -1;
+	}
+
+	memset(buf, 0, sizeof(* buf));
+
+	if (faData.ftLastWriteTime.dwLowDateTime ||
+		faData.ftLastWriteTime.dwHighDateTime)
+		buf->st_mtime = cvt_ft2ut( & faData.ftLastWriteTime );
+
+	if (faData.ftLastAccessTime.dwLowDateTime ||
+		faData.ftLastAccessTime.dwHighDateTime)
+		buf->st_atime = cvt_ft2ut(& faData.ftLastAccessTime);
+	else
+		buf->st_atime = buf->st_mtime;
+
+	if (faData.ftCreationTime.dwLowDateTime ||
+		faData.ftCreationTime.dwHighDateTime)
+		buf->st_ctime = cvt_ft2ut(& faData.ftCreationTime);
+	else
+		buf->st_ctime = buf->st_mtime;
+
+	buf->st_mode  = cvt_attr2uxmode(faData.dwFileAttributes, name);
+	buf->st_nlink = 1;
+
+	buf->st_size = ((__int64) faData.nFileSizeHigh) << 32 |
+				   (__int64)(faData.nFileSizeLow);
+
+	return 0;
+}
+
+int
+_pgfstat64(int fileno, struct stat *buf)
+{
+	const char filepath[MAX_PATH];
+	FILE_NAME_INFO nameInfo;
+
+	if (GetFileInformationByHandleEx((HANDLE) _get_osfhandle(fileno), 
+									 FileNameInfo,
+									 &nameInfo,
+									 sizeof(nameInfo)))
+	{
+		sprintf(filepath, "%ws", nameInfo.FileName);
+		return _pgstat64(filepath, buf);
+	}
+	else
+		return -1;
+}
+
+#endif /* WIN32 */
-- 
2.11.0

