From 6154e35d35515a7536524b79cb7ccd6a39d41afe Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 5 Mar 2023 11:24:51 +1300
Subject: [PATCH 02/11] Use pgoff_t in system call replacements on Windows.

All modern Unix systems have 64 bit off_t, but Windows does not.  Use
our pgoff_t type in our POSIX-style replacement functions (lseek(),
ftruncate(), pread(), pwrite() etc etc).  Also in closely related
functions like pg_pwrite_zeros().
---
 configure                       |  6 +++
 configure.ac                    |  1 +
 src/common/file_utils.c         |  4 +-
 src/include/common/file_utils.h |  4 +-
 src/include/port.h              |  2 +-
 src/include/port/pg_iovec.h     |  4 +-
 src/include/port/win32_port.h   | 23 ++++++++++--
 src/port/meson.build            |  1 +
 src/port/preadv.c               |  2 +-
 src/port/pwritev.c              |  2 +-
 src/port/win32ftruncate.c       | 65 +++++++++++++++++++++++++++++++++
 src/port/win32pread.c           |  3 +-
 src/port/win32pwrite.c          |  3 +-
 src/tools/msvc/Mkvcbuild.pm     |  1 +
 14 files changed, 106 insertions(+), 15 deletions(-)
 create mode 100644 src/port/win32ftruncate.c

diff --git a/configure b/configure
index 15daccc87f..47ba18491c 100755
--- a/configure
+++ b/configure
@@ -16537,6 +16537,12 @@ esac
  ;;
 esac
 
+  case " $LIBOBJS " in
+  *" win32ftruncate.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS win32ftruncate.$ac_objext"
+ ;;
+esac
+
   case " $LIBOBJS " in
   *" win32getrusage.$ac_objext "* ) ;;
   *) LIBOBJS="$LIBOBJS win32getrusage.$ac_objext"
diff --git a/configure.ac b/configure.ac
index 97f5be6c73..2b3b1b4dca 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1905,6 +1905,7 @@ if test "$PORTNAME" = "win32"; then
   AC_LIBOBJ(win32env)
   AC_LIBOBJ(win32error)
   AC_LIBOBJ(win32fdatasync)
+  AC_LIBOBJ(win32ftruncate)
   AC_LIBOBJ(win32getrusage)
   AC_LIBOBJ(win32link)
   AC_LIBOBJ(win32ntdll)
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 74833c4acb..7a63434bc4 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -469,7 +469,7 @@ get_dirent_type(const char *path,
  * error is returned, it is unspecified how much has been written.
  */
 ssize_t
-pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
+pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, pgoff_t offset)
 {
 	struct iovec iov_copy[PG_IOV_MAX];
 	ssize_t		sum = 0;
@@ -538,7 +538,7 @@ pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
  * is returned with errno set.
  */
 ssize_t
-pg_pwrite_zeros(int fd, size_t size, off_t offset)
+pg_pwrite_zeros(int fd, size_t size, pgoff_t offset)
 {
 	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth BLCKSZ */
 	void	   *zerobuf_addr = unconstify(PGIOAlignedBlock *, &zbuffer)->data;
diff --git a/src/include/common/file_utils.h b/src/include/common/file_utils.h
index b7efa1226d..534277b12d 100644
--- a/src/include/common/file_utils.h
+++ b/src/include/common/file_utils.h
@@ -42,8 +42,8 @@ extern PGFileType get_dirent_type(const char *path,
 extern ssize_t pg_pwritev_with_retry(int fd,
 									 const struct iovec *iov,
 									 int iovcnt,
-									 off_t offset);
+									 pgoff_t offset);
 
-extern ssize_t pg_pwrite_zeros(int fd, size_t size, off_t offset);
+extern ssize_t pg_pwrite_zeros(int fd, size_t size, pgoff_t offset);
 
 #endif							/* FILE_UTILS_H */
diff --git a/src/include/port.h b/src/include/port.h
index a88d403483..f7707a390e 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -368,7 +368,7 @@ extern FILE *pgwin32_popen(const char *command, const char *type);
  * When necessary, these routines are provided by files in src/port/.
  */
 
-/* Type to use with fseeko/ftello */
+/* Type to use with lseek/ftruncate/pread/fseeko/ftello */
 #ifndef WIN32					/* WIN32 is handled in port/win32_port.h */
 #define pgoff_t off_t
 #endif
diff --git a/src/include/port/pg_iovec.h b/src/include/port/pg_iovec.h
index 689799c425..c762fab662 100644
--- a/src/include/port/pg_iovec.h
+++ b/src/include/port/pg_iovec.h
@@ -43,13 +43,13 @@ struct iovec
 #if HAVE_DECL_PREADV
 #define pg_preadv preadv
 #else
-extern ssize_t pg_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset);
+extern ssize_t pg_preadv(int fd, const struct iovec *iov, int iovcnt, pgoff_t offset);
 #endif
 
 #if HAVE_DECL_PWRITEV
 #define pg_pwritev pwritev
 #else
-extern ssize_t pg_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);
+extern ssize_t pg_pwritev(int fd, const struct iovec *iov, int iovcnt, pgoff_t offset);
 #endif
 
 #endif							/* PG_IOVEC_H */
diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h
index 58965e0dfd..c757687386 100644
--- a/src/include/port/win32_port.h
+++ b/src/include/port/win32_port.h
@@ -76,11 +76,19 @@
 #undef fstat
 #undef stat
 
+/* and likewise for lseek hack */
+#define lseek microsoft_native_lseek
+#include <io.h>
+#undef lseek
+
+/* and also ftruncate, as defined by MinGW headers with 32 bit offset */
+#define ftruncate mingw_native_ftruncate
+#include <unistd.h>
+#undef ftruncate
+
 /* Must be here to avoid conflicting with prototype in windows.h */
 #define mkdir(a,b)	mkdir(a)
 
-#define ftruncate(a,b)	chsize(a,b)
-
 /* Windows doesn't have fsync() as such, use _commit() */
 #define fsync(fd) _commit(fd)
 
@@ -219,6 +227,7 @@ extern int	_pgfseeko64(FILE *stream, pgoff_t offset, int origin);
 extern pgoff_t _pgftello64(FILE *stream);
 #define fseeko(stream, offset, origin) _pgfseeko64(stream, offset, origin)
 #define ftello(stream) _pgftello64(stream)
+#define lseek(fd, offset, origin) _lseeki64((fd), (offset), (origin))
 #else
 #ifndef fseeko
 #define fseeko(stream, offset, origin) fseeko64(stream, offset, origin)
@@ -226,7 +235,13 @@ extern pgoff_t _pgftello64(FILE *stream);
 #ifndef ftello
 #define ftello(stream) ftello64(stream)
 #endif
+#ifndef lseek
+#define lseek(fd, offset, origin) _lseeki64((fd), (offset), (origin))
 #endif
+#endif
+
+/* 64 bit ftruncate is in win32ftruncate.c */
+extern int ftruncate(int fd, pgoff_t length);
 
 /*
  *	Win32 also doesn't have symlinks, but we can emulate them with
@@ -586,9 +601,9 @@ typedef unsigned short mode_t;
 #endif
 
 /* in port/win32pread.c */
-extern ssize_t pg_pread(int fd, void *buf, size_t nbyte, off_t offset);
+extern ssize_t pg_pread(int fd, void *buf, size_t nbyte, pgoff_t offset);
 
 /* in port/win32pwrite.c */
-extern ssize_t pg_pwrite(int fd, const void *buf, size_t nbyte, off_t offset);
+extern ssize_t pg_pwrite(int fd, const void *buf, size_t nbyte, pgoff_t offset);
 
 #endif							/* PG_WIN32_PORT_H */
diff --git a/src/port/meson.build b/src/port/meson.build
index 24416b9bfc..54ce59806a 100644
--- a/src/port/meson.build
+++ b/src/port/meson.build
@@ -35,6 +35,7 @@ if host_system == 'windows'
     'win32error.c',
     'win32fdatasync.c',
     'win32fseek.c',
+    'win32ftruncate.c',
     'win32getrusage.c',
     'win32link.c',
     'win32ntdll.c',
diff --git a/src/port/preadv.c b/src/port/preadv.c
index e762283e67..6e5e92234f 100644
--- a/src/port/preadv.c
+++ b/src/port/preadv.c
@@ -19,7 +19,7 @@
 #include "port/pg_iovec.h"
 
 ssize_t
-pg_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset)
+pg_preadv(int fd, const struct iovec *iov, int iovcnt, pgoff_t offset)
 {
 	ssize_t		sum = 0;
 	ssize_t		part;
diff --git a/src/port/pwritev.c b/src/port/pwritev.c
index 519de45037..c430f99806 100644
--- a/src/port/pwritev.c
+++ b/src/port/pwritev.c
@@ -19,7 +19,7 @@
 #include "port/pg_iovec.h"
 
 ssize_t
-pg_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset)
+pg_pwritev(int fd, const struct iovec *iov, int iovcnt, pgoff_t offset)
 {
 	ssize_t		sum = 0;
 	ssize_t		part;
diff --git a/src/port/win32ftruncate.c b/src/port/win32ftruncate.c
new file mode 100644
index 0000000000..5e6d4f3e92
--- /dev/null
+++ b/src/port/win32ftruncate.c
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------------------------
+ *
+ * win32ftruncate.c
+ *	   Win32 ftruncate() replacement
+ *
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ *
+ * src/port/win32ftruncate.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef FRONTEND
+#include "postgres_fe.h"
+#else
+#include "postgres.h"
+#endif
+
+int
+ftruncate(int fd, pgoff_t length)
+{
+	HANDLE		handle;
+	pgoff_t		save_position;
+
+	/*
+	 * We can't use chsize() because it works with 32 bit off_t.  We can't use
+	 * _chsize_s() because it isn't available in MinGW.  So we have to use
+	 * SetEndOfFile(), but that works with the current position.  So we save
+	 * and restore it.
+	 */
+
+	handle = (HANDLE) _get_osfhandle(fd);
+	if (handle == INVALID_HANDLE_VALUE)
+	{
+		errno = EBADF;
+		return -1;
+	}
+
+	save_position = lseek(fd, 0, SEEK_CUR);
+	if (save_position < 0)
+		return -1;
+
+	if (lseek(fd, length, SEEK_SET) < 0)
+	{
+		int			save_errno = errno;
+		lseek(fd, save_position, SEEK_SET);
+		errno = save_errno;
+		return -1;
+	}
+
+	if (!SetEndOfFile(handle))
+	{
+		int			save_errno;
+
+		_dosmaperr(GetLastError());
+		save_errno = errno;
+		lseek(fd, save_position, SEEK_SET);
+		errno = save_errno;
+		return -1;
+	}
+	lseek(fd, save_position, SEEK_SET);
+
+	return 0;
+}
diff --git a/src/port/win32pread.c b/src/port/win32pread.c
index 905cf9f42b..6e6366faaa 100644
--- a/src/port/win32pread.c
+++ b/src/port/win32pread.c
@@ -17,7 +17,7 @@
 #include <windows.h>
 
 ssize_t
-pg_pread(int fd, void *buf, size_t size, off_t offset)
+pg_pread(int fd, void *buf, size_t size, pgoff_t offset)
 {
 	OVERLAPPED	overlapped = {0};
 	HANDLE		handle;
@@ -32,6 +32,7 @@ pg_pread(int fd, void *buf, size_t size, off_t offset)
 
 	/* Note that this changes the file position, despite not using it. */
 	overlapped.Offset = offset;
+	overlapped.OffsetHigh = offset >> 32;
 	if (!ReadFile(handle, buf, size, &result, &overlapped))
 	{
 		if (GetLastError() == ERROR_HANDLE_EOF)
diff --git a/src/port/win32pwrite.c b/src/port/win32pwrite.c
index 5dd10821cf..90dd93dbc5 100644
--- a/src/port/win32pwrite.c
+++ b/src/port/win32pwrite.c
@@ -17,7 +17,7 @@
 #include <windows.h>
 
 ssize_t
-pg_pwrite(int fd, const void *buf, size_t size, off_t offset)
+pg_pwrite(int fd, const void *buf, size_t size, pgoff_t offset)
 {
 	OVERLAPPED	overlapped = {0};
 	HANDLE		handle;
@@ -32,6 +32,7 @@ pg_pwrite(int fd, const void *buf, size_t size, off_t offset)
 
 	/* Note that this changes the file position, despite not using it. */
 	overlapped.Offset = offset;
+	overlapped.OffsetHigh = offset >> 32;
 	if (!WriteFile(handle, buf, size, &result, &overlapped))
 	{
 		_dosmaperr(GetLastError());
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 958206f315..4b96c2bb44 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -113,6 +113,7 @@ sub mkvcbuild
 	  win32env.c win32error.c
 	  win32fdatasync.c
 	  win32fseek.c
+	  win32ftruncate.c
 	  win32getrusage.c
 	  win32gettimeofday.c
 	  win32link.c
-- 
2.40.1

