On Thursday 06 March 2025 21:40:55 Lasse Collin wrote:
> On 2025-03-05 Pali Rohár wrote:
> > As the discussion started, I decided to I looked at these issues. I
> > have some WIP changes which defines 4 variants of each
> > fstat/stat/wstat function, then provides also fstat, stat and wstat
> > function declaration in header file via __MINGW_ASM_CALL redirect and
> > also exports symbols from import library. With this way it behaves in
> > the same way as ms _fstat/_stat/_wstat symbols and functions.  This
> > should address also the autoconf issue which is trying to use
> > functions without including header file, and also ensure that
> > "stat()" call will be 64-bit + 64-bit if both -D_TIME_BITS=64 and
> > -D_FILE_OFFSET_BITS=64 are defined. At the same time I added
> > emulation wrapper for _fstat64/_stat64/_wstat64 function via WinAPI
> > fallback for older msvcrt libraries which do not have them.
> 
> Sounds great. :-)
> 
> > Do you want to look at my changes or test them?
> 
> Yes, I'm interested. It might take a few days depending on how I have
> time.

Ok. I'm sending 10 patches in the attachment.

> > What I have not addressed is the bug in the _mingw_no_trailing_slash
> > function which you described in the other email.
> 
> Earlier I tried adding a check for malloc() failure and making stat()
> fail with ENOTDIR if a slash had been removed and the result wasn't a
> directory. These were fairly simple changes, so if you didn't implement
> those, I can try on top of your patches.

I let the whole slash function as is. Just I moved it into separate file
as it was duplicated on more places Feel free to fix / improve it.

> I'm not sure if it is worth adding support for removing more than one
> slash at this point. It would need some care since the code has to keep
> one slash in some cases (I don't want add new bugs).
> 
> There's also one more tiny bug in the slash removal: with double-byte
> code pages, the second byte of a double-byte character can be \. The
> correct way to handle it would be using IsDBCSLeadByteEx like in
> mingw-w64-crt/misc/dirname.c. That makes things more complicated. Or
> one could maybe convert to wide char and call wstat(), but that's more
> complicated too. I don't know if it's worth it in practice at this
> point (UCRT doesn't need this code).
> 
> One more tiny bug: mingw-w64-crt/misc/dirname.c calls AreFileApisANSI to
> determine the filename code page, but it doesn't handle the UCRT UTF-8
> locale case. I wonder if it made sense to have a separate file for
> "DWORD __mingw64_filesystem_cp(void)" which dirname.c and dirent.c
> could use. The UTF-8 locale without UTF-8 manifest is hopefully not a
> common use case though.
> 
> -- 
> Lasse Collin
>From 7d1ffbf5a023e20a54bccac4b3e1beeb099a921b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.ro...@gmail.com>
Date: Thu, 24 Oct 2024 21:09:47 +0200
Subject: [PATCH 01/10] crt: Provide emulation of _fstat64, _stat64 and
 _wstat64 functions

These functions are available since msvcr70.dll. For older msvcrt versions
provide emulation via _fstat32(), _stat32() and _wstat32() functions. These
functions have only truncated 32-bit file size and timestamp support. Real
64-bit non-truncated file size and timestamp information is retrieved via
the WinAPI calls. For _stat64() and _wstat64() use FindFirstFile() call and
for _fstat64() use GetFileInformationByHandle() call. Same thing is doing
msvcrt DLL library for other stat functions.

Functions _fstat64() and _stat64() are already used by mingw-w64 crt, so
ensure that they are present in every CRT import library.
---
 mingw-w64-crt/Makefile.am                  |  8 +++-
 mingw-w64-crt/include/filetime_to_time64.h | 13 ++++++
 mingw-w64-crt/lib-common/msvcrt.def.in     |  6 +--
 mingw-w64-crt/stdio/_fstat64.c             | 50 ++++++++++++++++++++++
 mingw-w64-crt/stdio/_stat64.c              | 48 +++++++++++++++++++++
 mingw-w64-crt/stdio/_wstat64.c             | 48 +++++++++++++++++++++
 6 files changed, 169 insertions(+), 4 deletions(-)
 create mode 100644 mingw-w64-crt/include/filetime_to_time64.h
 create mode 100644 mingw-w64-crt/stdio/_fstat64.c
 create mode 100644 mingw-w64-crt/stdio/_stat64.c
 create mode 100644 mingw-w64-crt/stdio/_wstat64.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 7ebbd7c306d8..1c0ea244bd0e 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -521,11 +521,14 @@ src_msvcrt32=\
   misc/wcstoumax.c \
   misc/wctob.c \
   stdio/_fseeki64.c \
+  stdio/_fstat64.c \
   stdio/_fstat64i32.c \
   stdio/_scprintf.c \
   stdio/_scwprintf.c \
+  stdio/_stat64.c \
   stdio/_vscprintf.c \
   stdio/_vscwprintf.c \
+  stdio/_wstat64.c \
   string/wcstok.c
 
 # Files included in libmsvcrt-os.a (for msvcrt.dll) on x86_64
@@ -773,10 +776,13 @@ src_pre_msvcr70=\
   misc/strtoumax.c \
   misc/wcstoimax.c \
   misc/wcstoumax.c \
+  stdio/_fstat64.c \
   stdio/_scprintf.c \
   stdio/_scwprintf.c \
+  stdio/_stat64.c \
   stdio/_vscprintf.c \
-  stdio/_vscwprintf.c
+  stdio/_vscwprintf.c \
+  stdio/_wstat64.c
 
 src_pre_msvcr71=\
   misc/_set_purecall_handler.c
diff --git a/mingw-w64-crt/include/filetime_to_time64.h b/mingw-w64-crt/include/filetime_to_time64.h
new file mode 100644
index 000000000000..2d8459844a5f
--- /dev/null
+++ b/mingw-w64-crt/include/filetime_to_time64.h
@@ -0,0 +1,13 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+static inline __time64_t filetime_to_time64(FILETIME *filetime)
+{
+    unsigned long long value = ((unsigned long long)filetime->dwHighDateTime << 32) | filetime->dwLowDateTime;
+    if (value == 0) return 0; /* 0 has special meaning - not set */
+    /* conversion from unsigned 64-bit FILETIME (1601-01-01 in 100-nanoseconds) to signed 64-bit UNIX timestamp (1970-01-01 in seconds) */
+    return (value - 116444736000000000LL) / 10000000;
+}
diff --git a/mingw-w64-crt/lib-common/msvcrt.def.in b/mingw-w64-crt/lib-common/msvcrt.def.in
index 0d1223af5adb..fe1c8164234c 100644
--- a/mingw-w64-crt/lib-common/msvcrt.def.in
+++ b/mingw-w64-crt/lib-common/msvcrt.def.in
@@ -1153,20 +1153,20 @@ F_I386(_chkesp)
 _ctime64
 _findfirst64
 _findnext64
-_fstat64
+F_NON_I386(_fstat64) ; i386 _fstat64 replaced by emu
 _ftime64
 _futime64
 _gmtime64
 _localtime64
 _mktime64
 F_X86_ANY(_osplatform DATA)
-_stat64
+F_NON_I386(_stat64) ; i386 _stat64 replaced by emu
 _time64
 _utime64
 _wctime64
 _wfindfirst64
 _wfindnext64
-_wstat64
+F_NON_I386(_wstat64) ; i386 _wstat64 replaced by emu
 _wutime64
 
 ; These symbols were added in Windows 2000 SP4 OS system version of msvcrt.dll
diff --git a/mingw-w64-crt/stdio/_fstat64.c b/mingw-w64-crt/stdio/_fstat64.c
new file mode 100644
index 000000000000..0c059499b7e7
--- /dev/null
+++ b/mingw-w64-crt/stdio/_fstat64.c
@@ -0,0 +1,50 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <io.h>
+#include <windows.h>
+
+#include "filetime_to_time64.h"
+
+static int __cdecl emu__fstat64(int fd, struct _stat64 *stat)
+{
+    BY_HANDLE_FILE_INFORMATION fi;
+    struct _stat32 st;
+    int ret = _fstat32(fd, &st);
+    if (ret != 0)
+        return ret;
+    stat->st_dev = st.st_dev;
+    stat->st_ino = st.st_ino;
+    stat->st_mode = st.st_mode;
+    stat->st_nlink = st.st_nlink;
+    stat->st_uid = st.st_uid;
+    stat->st_gid = st.st_gid;
+    stat->st_rdev = st.st_rdev;
+    if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &fi)) {
+        stat->st_size = ((_off64_t)fi.nFileSizeHigh << 32) | fi.nFileSizeLow;
+        stat->st_atime = filetime_to_time64(&fi.ftLastAccessTime);
+        stat->st_mtime = filetime_to_time64(&fi.ftLastWriteTime);
+        stat->st_ctime = filetime_to_time64(&fi.ftCreationTime);
+    } else {
+        stat->st_size = st.st_size; /* truncated value */
+        stat->st_atime = st.st_atime; /* truncated value */
+        stat->st_mtime = st.st_mtime; /* truncated value */
+        stat->st_ctime = st.st_ctime; /* truncated value */
+    }
+    return 0;
+}
+
+#define RETT int
+#define FUNC _fstat64
+#define ARGS int fd, struct _stat64 *stat
+#define CALL fd, stat
+#include "msvcrt_or_emu_glue.h"
+
+#undef fstat64
+int __attribute__ ((alias ("_fstat64"))) __cdecl fstat64(int, struct _stat64 *);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(_fstat64))))) (__cdecl *__MINGW_IMP_SYMBOL(fstat64))(int, struct _stat64 *);
diff --git a/mingw-w64-crt/stdio/_stat64.c b/mingw-w64-crt/stdio/_stat64.c
new file mode 100644
index 000000000000..71247f0e4eab
--- /dev/null
+++ b/mingw-w64-crt/stdio/_stat64.c
@@ -0,0 +1,48 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <windows.h>
+
+#include "filetime_to_time64.h"
+
+static int __cdecl emu__stat64(const char *path, struct _stat64 *stat)
+{
+    HANDLE handle;
+    WIN32_FIND_DATAA fi;
+    struct _stat32 st;
+    int ret = _stat32(path, &st);
+    if (ret != 0)
+        return ret;
+    stat->st_dev = st.st_dev;
+    stat->st_ino = st.st_ino;
+    stat->st_mode = st.st_mode;
+    stat->st_nlink = st.st_nlink;
+    stat->st_uid = st.st_uid;
+    stat->st_gid = st.st_gid;
+    stat->st_rdev = st.st_rdev;
+    handle = FindFirstFileA(path, &fi);
+    if (handle != INVALID_HANDLE_VALUE) {
+        FindClose(handle);
+        stat->st_size = ((_off64_t)fi.nFileSizeHigh << 32) | fi.nFileSizeLow;
+        stat->st_atime = filetime_to_time64(&fi.ftLastAccessTime);
+        stat->st_mtime = filetime_to_time64(&fi.ftLastWriteTime);
+        stat->st_ctime = filetime_to_time64(&fi.ftCreationTime);
+    } else {
+        stat->st_size = st.st_size; /* truncated value */
+        stat->st_atime = st.st_atime; /* truncated value */
+        stat->st_mtime = st.st_mtime; /* truncated value */
+        stat->st_ctime = st.st_ctime; /* truncated value */
+    }
+    return 0;
+}
+
+#define RETT int
+#define FUNC _stat64
+#define ARGS const char *path, struct _stat64 *stat
+#define CALL path, stat
+#include "msvcrt_or_emu_glue.h"
diff --git a/mingw-w64-crt/stdio/_wstat64.c b/mingw-w64-crt/stdio/_wstat64.c
new file mode 100644
index 000000000000..9d5a39ebc79d
--- /dev/null
+++ b/mingw-w64-crt/stdio/_wstat64.c
@@ -0,0 +1,48 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <windows.h>
+
+#include "filetime_to_time64.h"
+
+static int __cdecl emu__wstat64(const wchar_t *path, struct _stat64 *stat)
+{
+    HANDLE handle;
+    WIN32_FIND_DATAW fi;
+    struct _stat32 st;
+    int ret = _wstat32(path, &st);
+    if (ret != 0)
+        return ret;
+    stat->st_dev = st.st_dev;
+    stat->st_ino = st.st_ino;
+    stat->st_mode = st.st_mode;
+    stat->st_nlink = st.st_nlink;
+    stat->st_uid = st.st_uid;
+    stat->st_gid = st.st_gid;
+    stat->st_rdev = st.st_rdev;
+    handle = FindFirstFileW(path, &fi);
+    if (handle != INVALID_HANDLE_VALUE) {
+        FindClose(handle);
+        stat->st_size = ((_off64_t)fi.nFileSizeHigh << 32) | fi.nFileSizeLow;
+        stat->st_atime = filetime_to_time64(&fi.ftLastAccessTime);
+        stat->st_mtime = filetime_to_time64(&fi.ftLastWriteTime);
+        stat->st_ctime = filetime_to_time64(&fi.ftCreationTime);
+    } else {
+        stat->st_size = st.st_size; /* truncated value */
+        stat->st_atime = st.st_atime; /* truncated value */
+        stat->st_mtime = st.st_mtime; /* truncated value */
+        stat->st_ctime = st.st_ctime; /* truncated value */
+    }
+    return 0;
+}
+
+#define RETT int
+#define FUNC _wstat64
+#define ARGS const wchar_t *path, struct _stat64 *stat
+#define CALL path, stat
+#include "msvcrt_or_emu_glue.h"
-- 
2.20.1


>From e39dbc4bbd4c7af869170c0cfaa7757f7602eefa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.ro...@gmail.com>
Date: Sun, 2 Mar 2025 20:29:23 +0100
Subject: [PATCH 02/10] crt: Fix mingw-w64 emulation of _fstat64i32, _stat64i32
 and _wstat64i32 functions

All 64-bit CRT import libraries already provides _fstat64i32, _stat64i32
and _wstat64i32 function symbols. These symbols are either directly
exported from 64-bit CRT DLL library or def file contains alias to other
ABI compatible symbols (_fstat, _stat and _wstat).

Also all functions _fstat64i32, _stat64i32 and _wstat64i32 are provided by
msvcr80+ and UCRT, so ensure that mingw-w64 does not provide any
replacement for msvcr80+ import libraries. And ensure that libmingwex.a
library does not provide duplication of these symbols.

For 32-bit pre-msvcr80 CRT import libraries provides mingw-w64 emulation
via _fstat64, _stat64 and _wstat64, which just truncates 64-bit st_size
(returned by _*stat64 function) to 32-bit st_size (which is ABI of
_*stat64i32). Do not use any _mingw_no_trailing_slash workaround as for all
these functions with underscore prefix, it is expected that behavior is
same as for other stat functions with underscore prefix.
---
 mingw-w64-crt/Makefile.am         | 10 +++-
 mingw-w64-crt/stdio/_fstat64i32.c | 18 ++++---
 mingw-w64-crt/stdio/_stat64i32.c  | 81 +++++--------------------------
 mingw-w64-crt/stdio/_wstat64i32.c | 81 +++++--------------------------
 4 files changed, 41 insertions(+), 149 deletions(-)

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 1c0ea244bd0e..44ed43d48b79 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -526,9 +526,11 @@ src_msvcrt32=\
   stdio/_scprintf.c \
   stdio/_scwprintf.c \
   stdio/_stat64.c \
+  stdio/_stat64i32.c \
   stdio/_vscprintf.c \
   stdio/_vscwprintf.c \
   stdio/_wstat64.c \
+  stdio/_wstat64i32.c \
   string/wcstok.c
 
 # Files included in libmsvcrt-os.a (for msvcrt.dll) on x86_64
@@ -610,6 +612,8 @@ src_msvcrtarm32=\
   misc/__winitenv.c \
   stdio/_fstat64i32.c \
   stdio/_setmaxstdio.c \
+  stdio/_stat64i32.c \
+  stdio/_wstat64i32.c \
   stdio/gets.c
 
 if !ENABLE_SOFTMATH
@@ -809,6 +813,8 @@ src_pre_msvcr80=\
   stdio/_fseeki64.c \
   stdio/_fstat64i32.c \
   stdio/_ftelli64.c \
+  stdio/_stat64i32.c \
+  stdio/_wstat64i32.c \
   stdio/mingw_lock.c \
   string/wcstok.c
 
@@ -1005,8 +1011,8 @@ src_libmingwex=\
   \
   stdio/strtok_r.c \
   stdio/_Exit.c            stdio/_findfirst64i32.c   stdio/_findnext64i32.c \
-  stdio/_stat.c            stdio/_stat64i32.c        stdio/_wfindfirst64i32.c stdio/_wfindnext64i32.c \
-  stdio/_wstat.c           stdio/_wstat64i32.c       stdio/asprintf.c \
+  stdio/_stat.c            stdio/_wfindfirst64i32.c  stdio/_wfindnext64i32.c \
+  stdio/_wstat.c           stdio/asprintf.c \
   stdio/fopen64.c          stdio/fseeko32.c          stdio/fseeko64.c                                   stdio/ftello.c          \
   stdio/ftello64.c         stdio/ftruncate64.c       stdio/lltoa.c            stdio/lltow.c             stdio/lseek64.c         \
   \
diff --git a/mingw-w64-crt/stdio/_fstat64i32.c b/mingw-w64-crt/stdio/_fstat64i32.c
index 57cc492d7731..93871c2f4717 100644
--- a/mingw-w64-crt/stdio/_fstat64i32.c
+++ b/mingw-w64-crt/stdio/_fstat64i32.c
@@ -1,3 +1,9 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 
@@ -5,10 +11,8 @@ int __cdecl _fstat64i32(int _FileDes,struct _stat64i32 *_Stat)
 {
   struct _stat64 st;
   int ret=_fstat64(_FileDes,&st);
-  if (ret == -1) {
-    memset(_Stat,0,sizeof(struct _stat64i32));
-    return -1;
-  }
+  if (ret != 0)
+    return ret;
   _Stat->st_dev=st.st_dev;
   _Stat->st_ino=st.st_ino;
   _Stat->st_mode=st.st_mode;
@@ -16,10 +20,10 @@ int __cdecl _fstat64i32(int _FileDes,struct _stat64i32 *_Stat)
   _Stat->st_uid=st.st_uid;
   _Stat->st_gid=st.st_gid;
   _Stat->st_rdev=st.st_rdev;
-  _Stat->st_size=(_off_t) st.st_size; /* 32bit size */
+  _Stat->st_size=(_off_t) st.st_size; /* truncate 64-bit st_size to 32-bit */
   _Stat->st_atime=st.st_atime;
   _Stat->st_mtime=st.st_mtime;
   _Stat->st_ctime=st.st_ctime;
-  return ret;
+  return 0;
 }
-
+int (__cdecl *__MINGW_IMP_SYMBOL(_fstat64i32))(int, struct _stat64i32 *) = _fstat64i32;
diff --git a/mingw-w64-crt/stdio/_stat64i32.c b/mingw-w64-crt/stdio/_stat64i32.c
index 19adf5b0ee19..90e5ee6a644f 100644
--- a/mingw-w64-crt/stdio/_stat64i32.c
+++ b/mingw-w64-crt/stdio/_stat64i32.c
@@ -1,77 +1,18 @@
-#define __CRT__NO_INLINE
-#include <sys/stat.h>
-#include <stdlib.h>
-
 /**
- * Returns _path without trailing slash if any
- *
- * - if _path has no trailing slash, the function returns it
- * - if _path has a trailing slash, but is of the form C:/, then it returns it
- * - otherwise, the function creates a new string, which is a copy of _path
- *   without the trailing slash. It is then the responsibility of the caller
- *   to free it.
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
 
-static char*
-_mingw_no_trailing_slash (const char* _path)
-{
-  int len;
-  char *p;
-
-  p = (char*)_path;
-
-  if (_path && *_path) {
-    len = strlen (_path);
-
-    /* Ignore X:\ */
-
-    if (len <= 1 || ((len == 2 || len == 3) && _path[1] == ':'))
-      return p;
-
-    /* Check UNC \\abc\<name>\ */
-    if ((_path[0] == '\\' || _path[0] == '/')
-	&& (_path[1] == '\\' || _path[1] == '/'))
-      {
-	const char *r = &_path[2];
-	while (*r != 0 && *r != '\\' && *r != '/')
-	  ++r;
-	if (*r != 0)
-	  ++r;
-	if (*r == 0)
-	  return p;
-	while (*r != 0 && *r != '\\' && *r != '/')
-	  ++r;
-	if (*r != 0)
-	  ++r;
-	if (*r == 0)
-	  return p;
-      }
-
-    if (_path[len - 1] == '/' || _path[len - 1] == '\\')
-      {
-	p = (char*)malloc (len);
-	memcpy (p, _path, len - 1);
-	p[len - 1] = '\0';
-      }
-  }
-
-  return p;
-}
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
 
 int __cdecl _stat64i32(const char *_Name,struct _stat64i32 *_Stat)
 {
   struct _stat64 st;
-  char *_path = _mingw_no_trailing_slash(_Name);
-  
-  int ret=_stat64(_path,&st);
-
-  if (_path != _Name)
-    free(_path);
-
-  if (ret == -1) {
-    memset(_Stat,0,sizeof(struct _stat64i32));
-    return -1;
-  }
+  int ret=_stat64(_Name,&st);
+  if (ret != 0)
+    return ret;
   _Stat->st_dev=st.st_dev;
   _Stat->st_ino=st.st_ino;
   _Stat->st_mode=st.st_mode;
@@ -79,10 +20,10 @@ int __cdecl _stat64i32(const char *_Name,struct _stat64i32 *_Stat)
   _Stat->st_uid=st.st_uid;
   _Stat->st_gid=st.st_gid;
   _Stat->st_rdev=st.st_rdev;
-  _Stat->st_size=(_off_t) st.st_size;
+  _Stat->st_size=(_off_t) st.st_size; /* truncate 64-bit st_size to 32-bit */
   _Stat->st_atime=st.st_atime;
   _Stat->st_mtime=st.st_mtime;
   _Stat->st_ctime=st.st_ctime;
-  return ret;
+  return 0;
 }
-
+int (__cdecl *__MINGW_IMP_SYMBOL(_stat64i32))(const char *, struct _stat64i32 *) = _stat64i32;
diff --git a/mingw-w64-crt/stdio/_wstat64i32.c b/mingw-w64-crt/stdio/_wstat64i32.c
index d6ee07a3d52e..846ed576bdc9 100644
--- a/mingw-w64-crt/stdio/_wstat64i32.c
+++ b/mingw-w64-crt/stdio/_wstat64i32.c
@@ -1,77 +1,18 @@
-#define __CRT__NO_INLINE
-#include <sys/stat.h>
-#include <stdlib.h>
-
 /**
- * Returns _path without trailing slash if any
- *
- * - if _path has no trailing slash, the function returns it
- * - if _path has a trailing slash, but is of the form C:/, then it returns it
- * - otherwise, the function creates a new string, which is a copy of _path
- *   without the trailing slash. It is then the responsibility of the caller
- *   to free it.
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
 
-static wchar_t*
-_mingw_no_trailing_slash (const wchar_t* _path)
-{
-  int len;
-  wchar_t *p;
-
-  p = (wchar_t*)_path;
-
-  if (_path && *_path) {
-    len = wcslen (_path);
-
-    /* Ignore X:\ */
-
-    if (len <= 1 || ((len == 2 || len == 3) && _path[1] == L':'))
-      return p;
-
-    /* Check UNC \\abc\<name>\ */
-    if ((_path[0] == L'\\' || _path[0] == L'/')
-	&& (_path[1] == L'\\' || _path[1] == L'/'))
-      {
-	const wchar_t *r = &_path[2];
-	while (*r != 0 && *r != L'\\' && *r != L'/')
-	  ++r;
-	if (*r != 0)
-	  ++r;
-	if (*r == 0)
-	  return p;
-	while (*r != 0 && *r != L'\\' && *r != L'/')
-	  ++r;
-	if (*r != 0)
-	  ++r;
-	if (*r == 0)
-	  return p;
-      }
-
-    if (_path[len - 1] == L'/' || _path[len - 1] == L'\\')
-      {
-	p = (wchar_t*)malloc (len * sizeof(wchar_t));
-	memcpy (p, _path, (len - 1) * sizeof(wchar_t));
-	p[len - 1] = L'\0';
-      }
-  }
-
-  return p;
-}
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
 
 int __cdecl _wstat64i32(const wchar_t *_Name,struct _stat64i32 *_Stat)
 {
   struct _stat64 st;
-  wchar_t *_path = _mingw_no_trailing_slash(_Name);
-  
-  int ret=_wstat64(_path,&st);
-
-  if (_path != _Name)
-    free(_path);
-
-  if (ret == -1) {
-    memset(_Stat,0,sizeof(struct _stat64i32));
-    return -1;
-  }
+  int ret=_wstat64(_Name,&st);
+  if (ret != 0)
+    return ret;
   _Stat->st_dev=st.st_dev;
   _Stat->st_ino=st.st_ino;
   _Stat->st_mode=st.st_mode;
@@ -79,10 +20,10 @@ int __cdecl _wstat64i32(const wchar_t *_Name,struct _stat64i32 *_Stat)
   _Stat->st_uid=st.st_uid;
   _Stat->st_gid=st.st_gid;
   _Stat->st_rdev=st.st_rdev;
-  _Stat->st_size=(_off_t) st.st_size;
+  _Stat->st_size=(_off_t) st.st_size; /* truncate 64-bit st_size to 32-bit */
   _Stat->st_atime=st.st_atime;
   _Stat->st_mtime=st.st_mtime;
   _Stat->st_ctime=st.st_ctime;
-  return ret;
+  return 0;
 }
-
+int (__cdecl *__MINGW_IMP_SYMBOL(_wstat64i32))(const wchar_t *, struct _stat64i32 *) = _wstat64i32;
-- 
2.20.1


>From d94e04c869a1b2014320e4da1e1d6f965615f842 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.ro...@gmail.com>
Date: Wed, 5 Mar 2025 23:10:39 +0100
Subject: [PATCH 03/10] crt: Provide emulation of _fstat32i64, _stat32i64 and
 _wstat32i64 functions

Functions _fstat32i64 (alias of _fstati64), _stat32i64 (alias of _stati64)
and _wstat32i64 (alias of _wstati64) are available since msvcrt40.dll.
These functions returns 64-bit st_size and 32-bit file timestamps.

For pre-msvcrt40 CRT import libraries provides mingw-w64 emulation of those
functions via the _fstat64, _stat64 and _stat64 functions, which returns
both st_size and file timestamps in 64-bit precision.
---
 mingw-w64-crt/Makefile.am         |  3 +++
 mingw-w64-crt/stdio/_fstat32i64.c | 33 +++++++++++++++++++++++++++++++
 mingw-w64-crt/stdio/_stat32i64.c  | 33 +++++++++++++++++++++++++++++++
 mingw-w64-crt/stdio/_wstat32i64.c | 33 +++++++++++++++++++++++++++++++
 4 files changed, 102 insertions(+)
 create mode 100644 mingw-w64-crt/stdio/_fstat32i64.c
 create mode 100644 mingw-w64-crt/stdio/_stat32i64.c
 create mode 100644 mingw-w64-crt/stdio/_wstat32i64.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 44ed43d48b79..db3f0d565e89 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -762,6 +762,9 @@ src_pre_msvcrt40=\
   misc/_dstbias.c \
   misc/dummy__setusermatherr.c \
   stdio/_filelengthi64.c \
+  stdio/_fstat32i64.c \
+  stdio/_stat32i64.c \
+  stdio/_wstat32i64.c \
   stdio/fgetpos.c \
   stdio/fsetpos.c
 
diff --git a/mingw-w64-crt/stdio/_fstat32i64.c b/mingw-w64-crt/stdio/_fstat32i64.c
new file mode 100644
index 000000000000..a30dee49d47f
--- /dev/null
+++ b/mingw-w64-crt/stdio/_fstat32i64.c
@@ -0,0 +1,33 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+
+int __cdecl _fstat32i64(int _FileDes,struct _stat32i64 *_Stat)
+{
+  struct _stat64 st;
+  int ret=_fstat64(_FileDes,&st);
+  if (ret != 0)
+    return ret;
+  _Stat->st_dev=st.st_dev;
+  _Stat->st_ino=st.st_ino;
+  _Stat->st_mode=st.st_mode;
+  _Stat->st_nlink=st.st_nlink;
+  _Stat->st_uid=st.st_uid;
+  _Stat->st_gid=st.st_gid;
+  _Stat->st_rdev=st.st_rdev;
+  _Stat->st_size=st.st_size;
+  _Stat->st_atime=(__time32_t) st.st_atime; /* truncate 64-bit st_atime to 32-bit */
+  _Stat->st_mtime=(__time32_t) st.st_mtime; /* truncate 64-bit st_mtime to 32-bit */
+  _Stat->st_ctime=(__time32_t) st.st_ctime; /* truncate 64-bit st_ctime to 32-bit */
+  return 0;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(_fstat32i64))(int, struct _stat32i64 *) = _fstat32i64;
+
+#undef _fstati64
+int __attribute__ ((alias ("_fstat32i64"))) __cdecl _fstati64(int, struct _stat32i64 *);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(_fstat32i64))))) (__cdecl *__MINGW_IMP_SYMBOL(_fstati64))(int, struct _stat32i64 *);
diff --git a/mingw-w64-crt/stdio/_stat32i64.c b/mingw-w64-crt/stdio/_stat32i64.c
new file mode 100644
index 000000000000..b83142f2a1df
--- /dev/null
+++ b/mingw-w64-crt/stdio/_stat32i64.c
@@ -0,0 +1,33 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+
+int __cdecl _stat32i64(const char *_Name,struct _stat32i64 *_Stat)
+{
+  struct _stat64 st;
+  int ret=_stat64(_Name,&st);
+  if (ret != 0)
+    return ret;
+  _Stat->st_dev=st.st_dev;
+  _Stat->st_ino=st.st_ino;
+  _Stat->st_mode=st.st_mode;
+  _Stat->st_nlink=st.st_nlink;
+  _Stat->st_uid=st.st_uid;
+  _Stat->st_gid=st.st_gid;
+  _Stat->st_rdev=st.st_rdev;
+  _Stat->st_size=st.st_size;
+  _Stat->st_atime=(__time32_t) st.st_atime; /* truncate 64-bit st_atime to 32-bit */
+  _Stat->st_mtime=(__time32_t) st.st_mtime; /* truncate 64-bit st_mtime to 32-bit */
+  _Stat->st_ctime=(__time32_t) st.st_ctime; /* truncate 64-bit st_ctime to 32-bit */
+  return 0;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(_stat32i64))(const char *, struct _stat32i64 *) = _stat32i64;
+
+#undef _stati64
+int __attribute__ ((alias ("_stat32i64"))) __cdecl _stati64(const char *, struct _stat32i64 *);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(_stat32i64))))) (__cdecl *__MINGW_IMP_SYMBOL(_stati64))(const char *, struct _stat32i64 *);
diff --git a/mingw-w64-crt/stdio/_wstat32i64.c b/mingw-w64-crt/stdio/_wstat32i64.c
new file mode 100644
index 000000000000..a584363f0ce9
--- /dev/null
+++ b/mingw-w64-crt/stdio/_wstat32i64.c
@@ -0,0 +1,33 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+
+int __cdecl _wstat32i64(const wchar_t *_Name,struct _stat32i64 *_Stat)
+{
+  struct _stat64 st;
+  int ret=_wstat64(_Name,&st);
+  if (ret != 0)
+    return ret;
+  _Stat->st_dev=st.st_dev;
+  _Stat->st_ino=st.st_ino;
+  _Stat->st_mode=st.st_mode;
+  _Stat->st_nlink=st.st_nlink;
+  _Stat->st_uid=st.st_uid;
+  _Stat->st_gid=st.st_gid;
+  _Stat->st_rdev=st.st_rdev;
+  _Stat->st_size=st.st_size;
+  _Stat->st_atime=(__time32_t) st.st_atime; /* truncate 64-bit st_atime to 32-bit */
+  _Stat->st_mtime=(__time32_t) st.st_mtime; /* truncate 64-bit st_mtime to 32-bit */
+  _Stat->st_ctime=(__time32_t) st.st_ctime; /* truncate 64-bit st_ctime to 32-bit */
+  return 0;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(_wstat32i64))(const wchar_t *, struct _stat32i64 *) = _wstat32i64;
+
+#undef _wstati64
+int __attribute__ ((alias ("_wstat32i64"))) __cdecl _wstati64(const wchar_t *, struct _stat32i64 *);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(_wstat32i64))))) (__cdecl *__MINGW_IMP_SYMBOL(_wstati64))(const wchar_t *, struct _stat32i64 *);
-- 
2.20.1


>From 716b162cbba5de29b25c54aabe88555e8f5257c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.ro...@gmail.com>
Date: Thu, 6 Mar 2025 23:42:39 +0100
Subject: [PATCH 04/10] headers: Remove broken inline stat() and fstat()
 functions

Correct versions are provided by non-inline variants. To prevent code
duplication and maintenance, remove those inline definitions completely
instead of fixing them.
---
 mingw-w64-headers/crt/sys/stat.h | 84 --------------------------------
 1 file changed, 84 deletions(-)

diff --git a/mingw-w64-headers/crt/sys/stat.h b/mingw-w64-headers/crt/sys/stat.h
index 5a85c2c483b5..343bd1178a01 100644
--- a/mingw-w64-headers/crt/sys/stat.h
+++ b/mingw-w64-headers/crt/sys/stat.h
@@ -185,90 +185,6 @@ int __cdecl fstat(int _Desc,struct stat *_Stat);
 int __cdecl stat(const char *_Filename,struct stat *_Stat);
 int __cdecl wstat(const wchar_t *_Filename,struct stat *_Stat);
 #endif
-
-#ifndef __CRT__NO_INLINE
-#ifdef _USE_32BIT_TIME_T
-__CRT_INLINE int __cdecl
- fstat(int _Desc,struct stat *_Stat) {
-  struct _stat32 st;
-  int __ret=_fstat32(_Desc,&st);
-  if (__ret == -1) {
-    memset(_Stat,0,sizeof(struct stat));
-    return -1;
-  }
-  /* struct stat and struct _stat32
-     are the same for this case. */
-  memcpy(_Stat, &st, sizeof(struct _stat32));
-  return __ret;
-}
-/* Disable it for making sure trailing slash issue is fixed.  */
-#if 0
-__CRT_INLINE int __cdecl
- stat(const char *_Filename,struct stat *_Stat) {
-  struct _stat32 st;
-  int __ret=_stat32(_Filename,&st);
-  if (__ret == -1) {
-    memset(_Stat,0,sizeof(struct stat));
-    return -1;
-  }
-  /* struct stat and struct _stat32
-     are the same for this case. */
-  memcpy(_Stat, &st, sizeof(struct _stat32));
-  return __ret;
-}
-#endif
-#else
-__CRT_INLINE int __cdecl
- fstat(int _Desc,struct stat *_Stat) {
-  struct _stat64 st;
-  int __ret=_fstat64(_Desc,&st);
-  if (__ret == -1) {
-    memset(_Stat,0,sizeof(struct stat));
-    return -1;
-  }
-  /* struct stat and struct _stat64i32
-     are the same for this case. */
-  _Stat->st_dev=st.st_dev;
-  _Stat->st_ino=st.st_ino;
-  _Stat->st_mode=st.st_mode;
-  _Stat->st_nlink=st.st_nlink;
-  _Stat->st_uid=st.st_uid;
-  _Stat->st_gid=st.st_gid;
-  _Stat->st_rdev=st.st_rdev;
-  _Stat->st_size=(_off_t) st.st_size;
-  _Stat->st_atime=st.st_atime;
-  _Stat->st_mtime=st.st_mtime;
-  _Stat->st_ctime=st.st_ctime;
-  return __ret;
-}
-/* Disable it for making sure trailing slash issue is fixed.  */
-#if 0
-__CRT_INLINE int __cdecl
- stat(const char *_Filename,struct stat *_Stat) {
-  struct _stat64 st;
-  int __ret=_stat64(_Filename,&st);
-  if (__ret == -1) {
-    memset(_Stat,0,sizeof(struct stat));
-    return -1;
-  }
-  /* struct stat and struct _stat64i32
-     are the same for this case. */
-  _Stat->st_dev=st.st_dev;
-  _Stat->st_ino=st.st_ino;
-  _Stat->st_mode=st.st_mode;
-  _Stat->st_nlink=st.st_nlink;
-  _Stat->st_uid=st.st_uid;
-  _Stat->st_gid=st.st_gid;
-  _Stat->st_rdev=st.st_rdev;
-  _Stat->st_size=(_off_t) st.st_size;
-  _Stat->st_atime=st.st_atime;
-  _Stat->st_mtime=st.st_mtime;
-  _Stat->st_ctime=st.st_ctime;
-  return __ret;
-}
-#endif
-#endif /* _USE_32BIT_TIME_T */
-#endif /* __CRT__NO_INLINE */
 #endif /* !RC_INVOKED && !NO_OLDNAMES */
 
 #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
-- 
2.20.1


>From a3232dba329b2cefc3ffd27655b9e429ec5b3a1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.ro...@gmail.com>
Date: Mon, 3 Mar 2025 19:39:16 +0100
Subject: [PATCH 05/10] headers: Remove inline definition of _fstat64i32() and
 _stat64i32() functions

These functions in sys/stat.h are defined as wrappers around _fstat64() and
_stat64() functions. But msvcr80+ and UCRT DLL libraries provides native
_fstat64i32() and _stat64i32() functions and so it is not needed to use
inline fallback wrappers.

For pre-msvcr80 builds, mingw-w64 already provides fallback implementations
in all CRT import libraries.

So remove inline definition of _fstat64i32() and _stat64i32() functions as
they are not needed and for msvcr80+ just prevents using them natively.
---
 mingw-w64-headers/crt/sys/stat.h | 46 --------------------------------
 1 file changed, 46 deletions(-)

diff --git a/mingw-w64-headers/crt/sys/stat.h b/mingw-w64-headers/crt/sys/stat.h
index 343bd1178a01..7f2ab7ab0d03 100644
--- a/mingw-w64-headers/crt/sys/stat.h
+++ b/mingw-w64-headers/crt/sys/stat.h
@@ -71,55 +71,9 @@ extern "C" {
   _CRTIMP int __cdecl _fstat64(int _FileDes,struct _stat64 *_Stat);
   _CRTIMP int __cdecl _fstat32i64(int _FileDes,struct _stat32i64 *_Stat);
   int __cdecl _fstat64i32(int _FileDes,struct _stat64i32 *_Stat);
-#ifndef __CRT__NO_INLINE
-  __CRT_INLINE int __cdecl _fstat64i32(int _FileDes,struct _stat64i32 *_Stat)
-  {
-    struct _stat64 st;
-    int __ret=_fstat64(_FileDes,&st);
-    if (__ret == -1) {
-      memset(_Stat,0,sizeof(struct _stat64i32));
-      return -1;
-    }
-    _Stat->st_dev=st.st_dev;
-    _Stat->st_ino=st.st_ino;
-    _Stat->st_mode=st.st_mode;
-    _Stat->st_nlink=st.st_nlink;
-    _Stat->st_uid=st.st_uid;
-    _Stat->st_gid=st.st_gid;
-    _Stat->st_rdev=st.st_rdev;
-    _Stat->st_size=(_off_t) st.st_size;
-    _Stat->st_atime=st.st_atime;
-    _Stat->st_mtime=st.st_mtime;
-    _Stat->st_ctime=st.st_ctime;
-    return __ret;
-  }
-#endif /* __CRT__NO_INLINE */
   _CRTIMP int __cdecl _stat64(const char *_Name,struct _stat64 *_Stat);
   _CRTIMP int __cdecl _stat32i64(const char *_Name,struct _stat32i64 *_Stat);
   int __cdecl _stat64i32(const char *_Name,struct _stat64i32 *_Stat);
-#ifndef __CRT__NO_INLINE
-  __CRT_INLINE int __cdecl _stat64i32(const char *_Name,struct _stat64i32 *_Stat)
-  {
-    struct _stat64 st;
-    int __ret=_stat64(_Name,&st);
-    if (__ret == -1) {
-      memset(_Stat,0,sizeof(struct _stat64i32));
-      return -1;
-    }
-    _Stat->st_dev=st.st_dev;
-    _Stat->st_ino=st.st_ino;
-    _Stat->st_mode=st.st_mode;
-    _Stat->st_nlink=st.st_nlink;
-    _Stat->st_uid=st.st_uid;
-    _Stat->st_gid=st.st_gid;
-    _Stat->st_rdev=st.st_rdev;
-    _Stat->st_size=(_off_t) st.st_size;
-    _Stat->st_atime=st.st_atime;
-    _Stat->st_mtime=st.st_mtime;
-    _Stat->st_ctime=st.st_ctime;
-    return __ret;
-  }
-#endif /* __CRT__NO_INLINE */
 
 #ifndef _WSTAT_DEFINED
 #define _WSTAT_DEFINED
-- 
2.20.1


>From fd906099b973dee21b5b452708fa539eced53394 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.ro...@gmail.com>
Date: Mon, 3 Mar 2025 19:44:12 +0100
Subject: [PATCH 06/10] headers: Add missing _CRTIMP for
 _fstat64i32/_stat64i32/_wstat64i32 functions

All other _*stat* functions are already marked with _CRTIMP, so add it also for missing 3 functions.
---
 mingw-w64-headers/crt/sys/stat.h | 6 +++---
 mingw-w64-headers/crt/wchar.h    | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/mingw-w64-headers/crt/sys/stat.h b/mingw-w64-headers/crt/sys/stat.h
index 7f2ab7ab0d03..b972a0af4444 100644
--- a/mingw-w64-headers/crt/sys/stat.h
+++ b/mingw-w64-headers/crt/sys/stat.h
@@ -70,16 +70,16 @@ extern "C" {
   _CRTIMP int __cdecl _stat32(const char *_Name,struct _stat32 *_Stat);
   _CRTIMP int __cdecl _fstat64(int _FileDes,struct _stat64 *_Stat);
   _CRTIMP int __cdecl _fstat32i64(int _FileDes,struct _stat32i64 *_Stat);
-  int __cdecl _fstat64i32(int _FileDes,struct _stat64i32 *_Stat);
+  _CRTIMP int __cdecl _fstat64i32(int _FileDes,struct _stat64i32 *_Stat);
   _CRTIMP int __cdecl _stat64(const char *_Name,struct _stat64 *_Stat);
   _CRTIMP int __cdecl _stat32i64(const char *_Name,struct _stat32i64 *_Stat);
-  int __cdecl _stat64i32(const char *_Name,struct _stat64i32 *_Stat);
+  _CRTIMP int __cdecl _stat64i32(const char *_Name,struct _stat64i32 *_Stat);
 
 #ifndef _WSTAT_DEFINED
 #define _WSTAT_DEFINED
   _CRTIMP int __cdecl _wstat32(const wchar_t *_Name,struct _stat32 *_Stat);
   _CRTIMP int __cdecl _wstat32i64(const wchar_t *_Name,struct _stat32i64 *_Stat);
-  int __cdecl _wstat64i32(const wchar_t *_Name,struct _stat64i32 *_Stat);
+  _CRTIMP int __cdecl _wstat64i32(const wchar_t *_Name,struct _stat64i32 *_Stat);
   _CRTIMP int __cdecl _wstat64(const wchar_t *_Name,struct _stat64 *_Stat);
 #endif
 
diff --git a/mingw-w64-headers/crt/wchar.h b/mingw-w64-headers/crt/wchar.h
index a5821e4bd3d6..1b61f9d33f8f 100644
--- a/mingw-w64-headers/crt/wchar.h
+++ b/mingw-w64-headers/crt/wchar.h
@@ -258,7 +258,7 @@ _CRTIMP FILE *__cdecl __acrt_iob_func(unsigned index);
 
   _CRTIMP int __cdecl _wstat32(const wchar_t *_Name,struct _stat32 *_Stat);
   _CRTIMP int __cdecl _wstat32i64(const wchar_t *_Name,struct _stat32i64 *_Stat);
-  int __cdecl _wstat64i32(const wchar_t *_Name,struct _stat64i32 *_Stat);
+  _CRTIMP int __cdecl _wstat64i32(const wchar_t *_Name,struct _stat64i32 *_Stat);
   _CRTIMP int __cdecl _wstat64(const wchar_t *_Name,struct _stat64 *_Stat);
 #endif
 #endif
-- 
2.20.1


>From caefb1c5cc3be7e26fec94dbc771b18e0a9c3f35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.ro...@gmail.com>
Date: Tue, 4 Mar 2025 00:34:08 +0100
Subject: [PATCH 07/10] crt: Fix mingw-w64 emulation of POSIX stat* functions

POSIX struct stat has 4 variants in mingw-w64 based on the
_FILE_OFFSET_BITS and _USE_32BIT_TIME_T settings. st_size can be either
32-bit or 64-bit, and st_atime/st_mtime/st_ctime can also be 32-bit or
64-bit.

So for each ABI of struct stat there has to be separate POSIX stat function
which correctly fills either 32-bit or 64-bit st_* values.

To follow existing msvcrt/UCRT symbol naming convention, provide 4 symbols
for stat function: stat32, stat32i64, stat64i32 and stat64. And also
provide stat symbol as alias to stat32 on 32-bit systems or to stat64i32 on
64-bit systems. Same as existing msvcrt/UCRT ABI for _stat function.

Note that stat64 function is part of the Large File Specification but it
does not describe if the st_atime/st_mtime/st_ctime should be also 64-bit
or only 32-bit. msvcrt/UCRT ABI expects them to be also 64-bit.

Do same for wstat symbol, wide UTF-16 variant of stat.

At the same time remove old and broken stat and wstat symbols, they are
replaced by the new alias.

Extract existing and duplicated function _mingw_no_trailing_slash into new
file and rename it to __mingw_fix_stat_path which better describe its
purpose. Do same for wide variant __mingw_fix_wstat_path. These two
functions have some known bugs and will be fixed later.

This change is not fixing bugs in these functions, it is only fixing the
API/ABI incompatibility which comes from the _FILE_OFFSET_BITS and
_USE_32BIT_TIME_T settings.
---
 mingw-w64-crt/Makefile.am                     | 14 +++-
 mingw-w64-crt/def-include/crt-aliases.def.in  | 28 +++++++-
 .../api-ms-win-crt-filesystem-l1-1-0.def.in   | 13 ++++
 .../{_stat.c => __mingw_fix_stat_path.c}      | 71 +++----------------
 .../{_wstat.c => __mingw_fix_wstat_path.c}    | 70 +++---------------
 mingw-w64-crt/stdio/stat32.c                  | 29 ++++++++
 mingw-w64-crt/stdio/stat32i64.c               | 22 ++++++
 mingw-w64-crt/stdio/stat64.c                  | 23 ++++++
 mingw-w64-crt/stdio/stat64i32.c               | 29 ++++++++
 mingw-w64-crt/stdio/wstat32.c                 | 30 ++++++++
 mingw-w64-crt/stdio/wstat32i64.c              | 22 ++++++
 mingw-w64-crt/stdio/wstat64.c                 | 22 ++++++
 mingw-w64-crt/stdio/wstat64i32.c              | 30 ++++++++
 13 files changed, 273 insertions(+), 130 deletions(-)
 rename mingw-w64-crt/stdio/{_stat.c => __mingw_fix_stat_path.c} (42%)
 rename mingw-w64-crt/stdio/{_wstat.c => __mingw_fix_wstat_path.c} (45%)
 create mode 100644 mingw-w64-crt/stdio/stat32.c
 create mode 100644 mingw-w64-crt/stdio/stat32i64.c
 create mode 100644 mingw-w64-crt/stdio/stat64.c
 create mode 100644 mingw-w64-crt/stdio/stat64i32.c
 create mode 100644 mingw-w64-crt/stdio/wstat32.c
 create mode 100644 mingw-w64-crt/stdio/wstat32i64.c
 create mode 100644 mingw-w64-crt/stdio/wstat64.c
 create mode 100644 mingw-w64-crt/stdio/wstat64i32.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index db3f0d565e89..3f75c8958b71 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -181,6 +181,14 @@ src_msvcrt_common=\
   stdio/vsnwprintf_alias.c \
   stdio/vswprintf.c \
   stdio/vswprintf_alias.c \
+  stdio/stat32.c \
+  stdio/stat32i64.c \
+  stdio/stat64.c \
+  stdio/stat64i32.c \
+  stdio/wstat32.c \
+  stdio/wstat32i64.c \
+  stdio/wstat64.c \
+  stdio/wstat64i32.c \
   math/cbrt.c math/cbrtf.c \
   math/copysign.c math/copysignf.c \
   math/coshf.c \
@@ -1013,11 +1021,11 @@ src_libmingwex=\
   ssp/stpcpy_chk.c       ssp/strcpy_chk.c           ssp/strncat_chk.c       ssp/strncpy_chk.c \
   \
   stdio/strtok_r.c \
-  stdio/_Exit.c            stdio/_findfirst64i32.c   stdio/_findnext64i32.c \
-  stdio/_stat.c            stdio/_wfindfirst64i32.c  stdio/_wfindnext64i32.c \
-  stdio/_wstat.c           stdio/asprintf.c \
+  stdio/_Exit.c            stdio/_findfirst64i32.c   stdio/_findnext64i32.c   stdio/_wfindfirst64i32.c  stdio/_wfindnext64i32.c \
+  stdio/asprintf.c \
   stdio/fopen64.c          stdio/fseeko32.c          stdio/fseeko64.c                                   stdio/ftello.c          \
   stdio/ftello64.c         stdio/ftruncate64.c       stdio/lltoa.c            stdio/lltow.c             stdio/lseek64.c         \
+  stdio/__mingw_fix_stat_path.c stdio/__mingw_fix_wstat_path.c \
   \
   stdio/mingw_pformat.h    mingw_sformat.h           mingw_swformat.h \
   stdio/mingw_fprintf.c    stdio/mingw_fwprintf.c    stdio/mingw_fscanf.c     stdio/mingw_fwscanf.c     stdio/mingw_pformat.c   \
diff --git a/mingw-w64-crt/def-include/crt-aliases.def.in b/mingw-w64-crt/def-include/crt-aliases.def.in
index be873c0df376..e4a401c1ef3c 100644
--- a/mingw-w64-crt/def-include/crt-aliases.def.in
+++ b/mingw-w64-crt/def-include/crt-aliases.def.in
@@ -137,8 +137,11 @@ ADD_UNDERSCORE(spawnve)
 ADD_UNDERSCORE(spawnvp)
 ADD_UNDERSCORE(spawnvpe)
 #endif
-#ifndef CRTDLL
-; stat is provided by mingw to workaround trailing slash issue in _stat
+#ifdef UCRTBASE
+F32(stat == _stat32)
+F64(stat == _stat64i32)
+#else
+; stat for non-UCRT is provided by mingw to workaround trailing slash issue in _stat
 #endif
 #ifdef NO_STRCMPI_ALIAS
 ; Symbol _strcmpi is natively present and defined in the library def file
@@ -290,6 +293,18 @@ ADD_UNDERSCORE(popen)
 fgetpos64 == fgetpos
 fsetpos64 == fsetpos
 #endif
+#ifdef UCRTBASE
+stat64 == _stat64
+#else
+; stat for non-UCRT is provided by mingw to workaround trailing slash issue in _stat
+#endif
+#ifdef FIXED_SIZE_SYMBOLS
+#if !defined(NO_I64_FIXED_SIZE) && !defined(NO_FIXED_SIZE_64_ALIAS)
+F64(fstat64 == _fstati64)
+#endif
+#else
+fstat64 == _fstat64
+#endif
 
 ; This is list of symbol aliases for GNU functions which are not part of POSIX or ISO C
 strcasecmp == _stricmp
@@ -558,6 +573,15 @@ __ms_vwscanf == vwscanf
 ; This is list of additional symbol aliases not available in any library as neither native symbols nor aliases
 ; FIXME: check if these really are needed
 
+; This is wstat and wstat64 symbol available only in mingw but for a long time
+#ifdef UCRTBASE
+F32(wstat == _wstat32)
+F64(wstat == _wstat64i32)
+wstat64 == _wstat64
+#else
+; wstat for non-UCRT is provided by mingw to workaround trailing slash issue in _wstat
+#endif
+
 ; Origin of the symbol wcscmpi is unknown. This symbol is not present in
 ; crtdll.dll and neither in any msvcr* version. The only source of wcscmpi is
 ; wcstr.h header file from the Microsoft Visual C++ 1.0 (32-bit for NT) and
diff --git a/mingw-w64-crt/lib-common/api-ms-win-crt-filesystem-l1-1-0.def.in b/mingw-w64-crt/lib-common/api-ms-win-crt-filesystem-l1-1-0.def.in
index 684baf712dcf..a0665a3a3d31 100644
--- a/mingw-w64-crt/lib-common/api-ms-win-crt-filesystem-l1-1-0.def.in
+++ b/mingw-w64-crt/lib-common/api-ms-win-crt-filesystem-l1-1-0.def.in
@@ -39,6 +39,7 @@ F64(_fstati64 == _fstat64)
 _fstat32
 _fstat32i64
 _fstat64
+fstat64 == _fstat64
 _fstat64i32
 _fullpath
 _getdiskfree
@@ -53,14 +54,20 @@ _rmdir
 rmdir == _rmdir
 _splitpath
 _splitpath_s
+F32(stat == _stat32)
+F64(stat == _stat64i32)
 F32(_stat == _stat32)
 F64(_stat == _stat64i32)
 F32(_stati64 == _stat32i64)
 F64(_stati64 == _stat64)
 _stat32
+stat32 == _stat32
 _stat32i64
+stat32i64 == _stat32i64
 _stat64
+stat64 == _stat64
 _stat64i32
+stat64i32 == _stat64i32
 _umask
 umask == _umask
 _umask_s
@@ -96,14 +103,20 @@ _wrename
 _wrmdir
 _wsplitpath
 _wsplitpath_s
+F32(wstat == _wstat32)
+F64(wstat == _wstat64i32)
 F32(_wstat == _wstat32)
 F64(_wstat == _wstat64i32)
 F32(_wstati64 == _wstat32i64)
 F64(_wstati64 == _wstat64)
 _wstat32
+wstat32 == _wstat32
 _wstat32i64
+wstat32i64 == _wstat32i64
 _wstat64
+wstat64 == _wstat64
 _wstat64i32
+wstat64i32 == _wstat64i32
 _wunlink
 remove
 rename
diff --git a/mingw-w64-crt/stdio/_stat.c b/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
similarity index 42%
rename from mingw-w64-crt/stdio/_stat.c
rename to mingw-w64-crt/stdio/__mingw_fix_stat_path.c
index fbb985e389e9..1efb3fea3da9 100644
--- a/mingw-w64-crt/stdio/_stat.c
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
@@ -1,3 +1,9 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
@@ -12,8 +18,8 @@
  *   to free it.
  */
 
-static char*
-_mingw_no_trailing_slash (const char* _path)
+char* __mingw_fix_stat_path (const char* _path);
+char* __mingw_fix_stat_path (const char* _path)
 {
   int len;
   char *p;
@@ -57,64 +63,3 @@ _mingw_no_trailing_slash (const char* _path)
 
   return p;
 }
-/* FIXME: Relying on _USE_32BIT_TIME_T, which is a user-macro,
-during CRT compilation is plainly broken.  Need an appropriate
-implementation to provide users the ability of compiling the
-CRT only with 32-bit time_t behavior. */
-#if defined(_USE_32BIT_TIME_T)
-int __cdecl
-stat(const char *_Filename,struct stat *_Stat)
-{
-  struct _stat32 st;
-  char *_path = _mingw_no_trailing_slash(_Filename);
-
-  int ret=_stat32(_path,&st);
-
-  if (_path != _Filename)
-    free (_path);
-
-  if (ret == -1) {
-    memset(_Stat,0,sizeof(struct stat));
-    return -1;
-  }
-  /* struct stat and struct _stat32
-     are the same for this case. */
-  memcpy(_Stat, &st, sizeof(struct _stat32));
-  return ret;
-}
-#else
-int __cdecl
-stat(const char *_Filename,struct stat *_Stat)
-{
-  struct _stat64 st;
-  char *_path = _mingw_no_trailing_slash(_Filename);
-
-  int ret=_stat64(_path,&st);
-
-  if (_path != _Filename)
-    free (_path);
-
-  if (ret == -1) {
-    memset(_Stat,0,sizeof(struct stat));
-    return -1;
-  }
-  /* struct stat and struct _stat64i32
-     are the same for this case. */
-  _Stat->st_dev=st.st_dev;
-  _Stat->st_ino=st.st_ino;
-  _Stat->st_mode=st.st_mode;
-  _Stat->st_nlink=st.st_nlink;
-  _Stat->st_uid=st.st_uid;
-  _Stat->st_gid=st.st_gid;
-  _Stat->st_rdev=st.st_rdev;
-  _Stat->st_size=(_off_t) st.st_size;
-  _Stat->st_atime=st.st_atime;
-  _Stat->st_mtime=st.st_mtime;
-  _Stat->st_ctime=st.st_ctime;
-  return ret;
-}
-#endif
-
-/* Add __imp__fstat and __imp__stat symbols.  */
-int (*__MINGW_IMP_SYMBOL(stat))(const char *,struct stat *) = &stat;
-
diff --git a/mingw-w64-crt/stdio/_wstat.c b/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
similarity index 45%
rename from mingw-w64-crt/stdio/_wstat.c
rename to mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
index 08566168fbe5..a121911faf6c 100644
--- a/mingw-w64-crt/stdio/_wstat.c
+++ b/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
@@ -1,7 +1,12 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
-#include <malloc.h>
 
 /**
  * Returns _path without trailing slash if any
@@ -13,8 +18,8 @@
  *   to free it.
  */
 
-static wchar_t*
-_mingw_no_trailing_slash (const wchar_t* _path)
+wchar_t* __mingw_fix_stat_path (wchar_t* _path);
+wchar_t* __mingw_fix_stat_path (wchar_t* _path)
 {
   int len;
   wchar_t *p;
@@ -58,62 +63,3 @@ _mingw_no_trailing_slash (const wchar_t* _path)
 
   return p;
 }
-
-/* FIXME: Relying on _USE_32BIT_TIME_T, which is a user-macro,
-during CRT compilation is plainly broken.  Need an appropriate
-implementation to provide users the ability of compiling the
-CRT only with 32-bit time_t behavior. */
-#if defined(_USE_32BIT_TIME_T)
-int __cdecl
-wstat(const wchar_t *_Filename,struct stat *_Stat)
-{
-  struct _stat32 st;
-  wchar_t *_path = _mingw_no_trailing_slash(_Filename);
-
-  int ret=_wstat32(_path,&st);
-
-  if (_path != _Filename)
-    free (_path);
-
-  if (ret == -1) {
-    memset(_Stat,0,sizeof(struct stat));
-    return -1;
-  }
-  /* struct stat and struct _stat32
-     are the same for this case. */
-  memcpy(_Stat, &st, sizeof(struct _stat32));
-  return ret;
-}
-#else
-int __cdecl
-wstat(const wchar_t *_Filename,struct stat *_Stat)
-{
-  struct _stat64 st;
-  wchar_t *_path = _mingw_no_trailing_slash(_Filename);
-
-  int ret=_wstat64(_path,&st);
-
-  if (_path != _Filename)
-    free (_path);
-
-  if (ret == -1) {
-    memset(_Stat,0,sizeof(struct stat));
-    return -1;
-  }
-  /* struct stat and struct _stat64i32
-     are the same for this case. */
-  _Stat->st_dev=st.st_dev;
-  _Stat->st_ino=st.st_ino;
-  _Stat->st_mode=st.st_mode;
-  _Stat->st_nlink=st.st_nlink;
-  _Stat->st_uid=st.st_uid;
-  _Stat->st_gid=st.st_gid;
-  _Stat->st_rdev=st.st_rdev;
-  _Stat->st_size=(_off_t) st.st_size;
-  _Stat->st_atime=st.st_atime;
-  _Stat->st_mtime=st.st_mtime;
-  _Stat->st_ctime=st.st_ctime;
-  return ret;
-}
-#endif
-
diff --git a/mingw-w64-crt/stdio/stat32.c b/mingw-w64-crt/stdio/stat32.c
new file mode 100644
index 000000000000..be3e4694b83f
--- /dev/null
+++ b/mingw-w64-crt/stdio/stat32.c
@@ -0,0 +1,29 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <stdlib.h>
+
+char *__mingw_fix_stat_path(const char *_path);
+
+int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat);
+int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat)
+{
+  char *_path = __mingw_fix_stat_path(_Filename);
+  int ret = _stat32(_path, _Stat);
+  if (_path != _Filename)
+    free(_path);
+  return ret;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(stat32))(const char *, struct _stat32 *) = stat32;
+
+/* On 32-bit systems is stat() function ABI compatible with stat32() function */
+#ifndef _WIN64
+#undef stat
+int __attribute__ ((alias ("stat32"))) __cdecl stat(const char *name, struct stat *stat);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(stat32))))) (__cdecl *__MINGW_IMP_SYMBOL(stat))(const char *name, struct stat *stat);
+#endif
diff --git a/mingw-w64-crt/stdio/stat32i64.c b/mingw-w64-crt/stdio/stat32i64.c
new file mode 100644
index 000000000000..f853d7a3f25f
--- /dev/null
+++ b/mingw-w64-crt/stdio/stat32i64.c
@@ -0,0 +1,22 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <stdlib.h>
+
+char *__mingw_fix_stat_path(const char *_path);
+
+int __cdecl stat32i64(const char *_Filename, struct _stat32i64 *_Stat);
+int __cdecl stat32i64(const char *_Filename, struct _stat32i64 *_Stat)
+{
+  char *_path = __mingw_fix_stat_path(_Filename);
+  int ret = _stat32i64(_path, _Stat);
+  if (_path != _Filename)
+    free(_path);
+  return ret;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(stat32i64))(const char *, struct _stat32i64 *) = stat32i64;
diff --git a/mingw-w64-crt/stdio/stat64.c b/mingw-w64-crt/stdio/stat64.c
new file mode 100644
index 000000000000..2e1d66ebc3fd
--- /dev/null
+++ b/mingw-w64-crt/stdio/stat64.c
@@ -0,0 +1,23 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <stdlib.h>
+
+char *__mingw_fix_stat_path(const char *_path);
+
+#undef stat64
+int __cdecl stat64(const char *_Filename, struct _stat64 *_Stat);
+int __cdecl stat64(const char *_Filename, struct _stat64 *_Stat)
+{
+  char *_path = __mingw_fix_stat_path(_Filename);
+  int ret = _stat64(_path, _Stat);
+  if (_path != _Filename)
+    free(_path);
+  return ret;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(stat64))(const char *, struct _stat64 *) = stat64;
diff --git a/mingw-w64-crt/stdio/stat64i32.c b/mingw-w64-crt/stdio/stat64i32.c
new file mode 100644
index 000000000000..66f069722f12
--- /dev/null
+++ b/mingw-w64-crt/stdio/stat64i32.c
@@ -0,0 +1,29 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <stdlib.h>
+
+char *__mingw_fix_stat_path(const char *_path);
+
+int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat);
+int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat)
+{
+  char *_path = __mingw_fix_stat_path(_Filename);
+  int ret = _stat64i32(_path, _Stat);
+  if (_path != _Filename)
+    free(_path);
+  return ret;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(stat64i32))(const char *, struct _stat64i32 *) = stat64i32;
+
+/* On 64-bit systems is stat() function ABI compatible with stat64i32() function */
+#ifdef _WIN64
+#undef stat
+int __attribute__ ((alias ("stat64i32"))) __cdecl stat(const char *name, struct stat *stat);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(stat64i32))))) (__cdecl *__MINGW_IMP_SYMBOL(stat))(const char *name, struct stat *stat);
+#endif
diff --git a/mingw-w64-crt/stdio/wstat32.c b/mingw-w64-crt/stdio/wstat32.c
new file mode 100644
index 000000000000..41e3b1c85550
--- /dev/null
+++ b/mingw-w64-crt/stdio/wstat32.c
@@ -0,0 +1,30 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <stdlib.h>
+
+wchar_t *__mingw_fix_wstat_path(const wchar_t *_path);
+
+int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat);
+int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat)
+{
+  wchar_t *_path = __mingw_fix_wstat_path(_Filename);
+  int ret = _wstat32(_path, _Stat);
+  if (_path != _Filename)
+    free(_path);
+  return ret;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(wstat32))(const wchar_t *, struct _stat32 *) = wstat32;
+
+/* On 32-bit systems is wstat() function ABI compatible with wstat32() function */
+#ifndef _WIN64
+#undef stat
+#undef wstat
+int __attribute__ ((alias ("wstat32"))) __cdecl wstat(const wchar_t *name, struct stat *stat);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(wstat32))))) (__cdecl *__MINGW_IMP_SYMBOL(wstat))(const wchar_t *name, struct stat *stat);
+#endif
diff --git a/mingw-w64-crt/stdio/wstat32i64.c b/mingw-w64-crt/stdio/wstat32i64.c
new file mode 100644
index 000000000000..15acacb532ce
--- /dev/null
+++ b/mingw-w64-crt/stdio/wstat32i64.c
@@ -0,0 +1,22 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <stdlib.h>
+
+wchar_t *__mingw_fix_wstat_path(const wchar_t *_path);
+
+int __cdecl wstat32i64(const wchar_t *_Filename, struct _stat32i64 *_Stat);
+int __cdecl wstat32i64(const wchar_t *_Filename, struct _stat32i64 *_Stat)
+{
+  wchar_t *_path = __mingw_fix_wstat_path(_Filename);
+  int ret = _wstat32i64(_path, _Stat);
+  if (_path != _Filename)
+    free(_path);
+  return ret;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(wstat32i64))(const wchar_t *, struct _stat32i64 *) = wstat32i64;
diff --git a/mingw-w64-crt/stdio/wstat64.c b/mingw-w64-crt/stdio/wstat64.c
new file mode 100644
index 000000000000..a24d81cd244e
--- /dev/null
+++ b/mingw-w64-crt/stdio/wstat64.c
@@ -0,0 +1,22 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <stdlib.h>
+
+wchar_t *__mingw_fix_wstat_path(const wchar_t *_path);
+
+int __cdecl wstat64(const wchar_t *_Filename, struct _stat64 *_Stat);
+int __cdecl wstat64(const wchar_t *_Filename, struct _stat64 *_Stat)
+{
+  wchar_t *_path = __mingw_fix_wstat_path(_Filename);
+  int ret = _wstat64(_path, _Stat);
+  if (_path != _Filename)
+    free(_path);
+  return ret;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(wstat64))(const wchar_t *, struct _stat64 *) = wstat64;
diff --git a/mingw-w64-crt/stdio/wstat64i32.c b/mingw-w64-crt/stdio/wstat64i32.c
new file mode 100644
index 000000000000..5821cc464c38
--- /dev/null
+++ b/mingw-w64-crt/stdio/wstat64i32.c
@@ -0,0 +1,30 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#define __CRT__NO_INLINE
+#include <sys/stat.h>
+#include <stdlib.h>
+
+wchar_t *__mingw_fix_wstat_path(const wchar_t *_path);
+
+int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat);
+int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat)
+{
+  wchar_t *_path = __mingw_fix_wstat_path(_Filename);
+  int ret = _wstat64i32(_path, _Stat);
+  if (_path != _Filename)
+    free(_path);
+  return ret;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(wstat64i32))(const wchar_t *, struct _stat64i32 *) = wstat64i32;
+
+/* On 64-bit systems is wstat() function ABI compatible with wstat64i32() function */
+#ifdef _WIN64
+#undef stat
+#undef wstat
+int __attribute__ ((alias ("wstat64i32"))) __cdecl wstat(const wchar_t *name, struct stat *stat);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(wstat64i32))))) (__cdecl *__MINGW_IMP_SYMBOL(wstat))(const wchar_t *name, struct stat *stat);
+#endif
-- 
2.20.1


>From a4d02cd446a7a168c3388120e3d9abeede403589 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.ro...@gmail.com>
Date: Mon, 3 Mar 2025 01:13:07 +0100
Subject: [PATCH 08/10] headers: Fix declaration of POSIX stat/fstat and LFS
 stat64/fstat64 functions

Move LFS defines of stat64/fstat64 from _mingw_stat64.h to sys/stat.h.
Move struct stat from _mingw_stat64.h to sys/stat.h.

Fix definition of POSIX struct stat to follow both _FILE_OFFSET_BITS and
_USE_32BIT_TIME_T settings.

Fix declaration of POSIX stat(), fstat() and mingw-w64 wstat() functions to
follow all combinations of _FILE_OFFSET_BITS and _USE_32BIT_TIME_T settings.

Declare fstat() as a function instead of macro. Define it as alias via
__MINGW_ASM_CALL to one of the CRT _fstat* function based on
_FILE_OFFSET_BITS and _USE_32BIT_TIME_T settings.

In the same way declare stat() and wstat() as functions. But as alias to
one of the mingw-w64 (w)stat* function. This is needed because msvcrt
_(w)stat* functions have issue with trailing slash and requires mingw-w64
wrapper for POSIX compatibility.

Additionaly define struct stat64 for LFS functions stat64(), fstat64() and
mingw-w64 function wstat64(). And again declare them as a functions instead
of macro.

stat has to be declared as a function, not as a macro because it would
override effect of struct stat.
---
 mingw-w64-crt/misc/crtdll_fstat.c     |  1 +
 mingw-w64-crt/misc/crtdll_stat.c      |  1 +
 mingw-w64-crt/stdio/_fstat64.c        |  5 +-
 mingw-w64-crt/stdio/stat64.c          |  8 ++-
 mingw-w64-crt/stdio/wstat64.c         |  7 ++-
 mingw-w64-headers/crt/_mingw_stat64.h | 23 ++-------
 mingw-w64-headers/crt/sys/stat.h      | 74 +++++++++++++++++++--------
 7 files changed, 66 insertions(+), 53 deletions(-)

diff --git a/mingw-w64-crt/misc/crtdll_fstat.c b/mingw-w64-crt/misc/crtdll_fstat.c
index f0b3b748b0eb..cf51ffb6a760 100644
--- a/mingw-w64-crt/misc/crtdll_fstat.c
+++ b/mingw-w64-crt/misc/crtdll_fstat.c
@@ -4,6 +4,7 @@
  * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
 
+#define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include "crtdll_stat.h"
 
diff --git a/mingw-w64-crt/misc/crtdll_stat.c b/mingw-w64-crt/misc/crtdll_stat.c
index f17bfbe93488..944596556f44 100644
--- a/mingw-w64-crt/misc/crtdll_stat.c
+++ b/mingw-w64-crt/misc/crtdll_stat.c
@@ -4,6 +4,7 @@
  * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
 
+#define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include "crtdll_stat.h"
 
diff --git a/mingw-w64-crt/stdio/_fstat64.c b/mingw-w64-crt/stdio/_fstat64.c
index 0c059499b7e7..b8032ef7ace3 100644
--- a/mingw-w64-crt/stdio/_fstat64.c
+++ b/mingw-w64-crt/stdio/_fstat64.c
@@ -45,6 +45,5 @@ static int __cdecl emu__fstat64(int fd, struct _stat64 *stat)
 #define CALL fd, stat
 #include "msvcrt_or_emu_glue.h"
 
-#undef fstat64
-int __attribute__ ((alias ("_fstat64"))) __cdecl fstat64(int, struct _stat64 *);
-extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(_fstat64))))) (__cdecl *__MINGW_IMP_SYMBOL(fstat64))(int, struct _stat64 *);
+int __attribute__ ((alias ("_fstat64"))) __cdecl fstat64(int, struct stat64 *);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(_fstat64))))) (__cdecl *__MINGW_IMP_SYMBOL(fstat64))(int, struct stat64 *);
diff --git a/mingw-w64-crt/stdio/stat64.c b/mingw-w64-crt/stdio/stat64.c
index 2e1d66ebc3fd..096bae132d35 100644
--- a/mingw-w64-crt/stdio/stat64.c
+++ b/mingw-w64-crt/stdio/stat64.c
@@ -10,14 +10,12 @@
 
 char *__mingw_fix_stat_path(const char *_path);
 
-#undef stat64
-int __cdecl stat64(const char *_Filename, struct _stat64 *_Stat);
-int __cdecl stat64(const char *_Filename, struct _stat64 *_Stat)
+int __cdecl stat64(const char *_Filename, struct stat64 *_Stat)
 {
   char *_path = __mingw_fix_stat_path(_Filename);
-  int ret = _stat64(_path, _Stat);
+  int ret = _stat64(_path, (struct _stat64 *)_Stat);
   if (_path != _Filename)
     free(_path);
   return ret;
 }
-int (__cdecl *__MINGW_IMP_SYMBOL(stat64))(const char *, struct _stat64 *) = stat64;
+int (__cdecl *__MINGW_IMP_SYMBOL(stat64))(const char *, struct stat64 *) = stat64;
diff --git a/mingw-w64-crt/stdio/wstat64.c b/mingw-w64-crt/stdio/wstat64.c
index a24d81cd244e..399efcf72c3b 100644
--- a/mingw-w64-crt/stdio/wstat64.c
+++ b/mingw-w64-crt/stdio/wstat64.c
@@ -10,13 +10,12 @@
 
 wchar_t *__mingw_fix_wstat_path(const wchar_t *_path);
 
-int __cdecl wstat64(const wchar_t *_Filename, struct _stat64 *_Stat);
-int __cdecl wstat64(const wchar_t *_Filename, struct _stat64 *_Stat)
+int __cdecl wstat64(const wchar_t *_Filename, struct stat64 *_Stat)
 {
   wchar_t *_path = __mingw_fix_wstat_path(_Filename);
-  int ret = _wstat64(_path, _Stat);
+  int ret = _wstat64(_path, (struct _stat64 *)_Stat);
   if (_path != _Filename)
     free(_path);
   return ret;
 }
-int (__cdecl *__MINGW_IMP_SYMBOL(wstat64))(const wchar_t *, struct _stat64 *) = wstat64;
+int (__cdecl *__MINGW_IMP_SYMBOL(wstat64))(const wchar_t *, struct stat64 *) = wstat64;
diff --git a/mingw-w64-headers/crt/_mingw_stat64.h b/mingw-w64-headers/crt/_mingw_stat64.h
index 8ce097561116..84eabba9698b 100644
--- a/mingw-w64-headers/crt/_mingw_stat64.h
+++ b/mingw-w64-headers/crt/_mingw_stat64.h
@@ -1,5 +1,8 @@
 #ifndef _STAT_DEFINED
 
+/* __stat64 is needed for compatibility with msvc */
+#define __stat64 _stat64
+
 #ifdef _USE_32BIT_TIME_T
 #define _fstat _fstat32
 #define _fstati64 _fstat32i64
@@ -30,22 +33,6 @@
     __time32_t st_ctime;
   };
 
-#ifndef	NO_OLDNAMES
-  struct stat {
-    _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;
-    _off_t st_size;
-    time_t st_atime;
-    time_t st_mtime;
-    time_t st_ctime;
-  };
-#endif /* NO_OLDNAMES */
-
   struct _stat32i64 {
     _dev_t st_dev;
     _ino_t st_ino;
@@ -88,9 +75,5 @@
     __time64_t st_ctime;
   };
 
-#define __stat64 _stat64
-#define stat64   _stat64  /* for POSIX */
-#define fstat64  _fstat64 /* for POSIX */
-
 #define _STAT_DEFINED
 #endif /* _STAT_DEFINED */
diff --git a/mingw-w64-headers/crt/sys/stat.h b/mingw-w64-headers/crt/sys/stat.h
index b972a0af4444..b7884d89fae4 100644
--- a/mingw-w64-headers/crt/sys/stat.h
+++ b/mingw-w64-headers/crt/sys/stat.h
@@ -124,31 +124,63 @@ extern "C" {
 
 #endif
 
-#if !defined (RC_INVOKED) && !defined (NO_OLDNAMES)
-int __cdecl fstat(int _Desc,struct stat *_Stat);
-#ifdef _UCRT
-  __mingw_ovr int __cdecl stat(const char *_Filename,struct stat *_Stat)
-  {
-    return _stat(_Filename, (struct _stat *)_Stat);
-  }
-  __mingw_ovr int __cdecl wstat(const wchar_t *_Filename,struct stat *_Stat)
-  {
-    return _wstat(_Filename, (struct _stat *)_Stat);
-  }
-#else
-int __cdecl stat(const char *_Filename,struct stat *_Stat);
-int __cdecl wstat(const wchar_t *_Filename,struct stat *_Stat);
-#endif
-#endif /* !RC_INVOKED && !NO_OLDNAMES */
-
+#if !defined(NO_OLDNAMES) || defined(_POSIX)
+
+struct stat {
+  _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;
+  off_t st_size; /* off_t follows _FILE_OFFSET_BITS */
+  time_t st_atime; /* time_t follows _USE_32BIT_TIME_T */
+  time_t st_mtime;
+  time_t st_ctime;
+};
+
+#ifndef __CRT__NO_INLINE
 #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
 #ifdef _USE_32BIT_TIME_T
-#define stat _stat32i64
-#define fstat _fstat32i64
+int __cdecl fstat(int _Desc, struct stat *_Stat) __MINGW_ASM_CALL(_fstat32i64);
+int __cdecl stat(const char *_Filename, struct stat *_Stat) __MINGW_ASM_CALL(stat32i64);
+int __cdecl wstat(const wchar_t *_Filename, struct stat *_Stat) __MINGW_ASM_CALL(wstat32i64);
 #else
-#define stat _stat64
-#define fstat _fstat64
+int __cdecl fstat(int _Desc, struct stat *_Stat) __MINGW_ASM_CALL(_fstat64);
+int __cdecl stat(const char *_Filename, struct stat *_Stat) __MINGW_ASM_CALL(stat64);
+int __cdecl wstat(const wchar_t *_Filename, struct stat *_Stat) __MINGW_ASM_CALL(wstat64);
 #endif
+#else
+#ifdef _USE_32BIT_TIME_T
+int __cdecl fstat(int _Desc, struct stat *_Stat) __MINGW_ASM_CALL(_fstat32);
+int __cdecl stat(const char *_Filename, struct stat *_Stat) __MINGW_ASM_CALL(stat32);
+int __cdecl wstat(const wchar_t *_Filename, struct stat *_Stat) __MINGW_ASM_CALL(wstat32);
+#else
+int __cdecl fstat(int _Desc, struct stat *_Stat) __MINGW_ASM_CALL(_fstat64i32);
+int __cdecl stat(const char *_Filename, struct stat *_Stat) __MINGW_ASM_CALL(stat64i32);
+int __cdecl wstat(const wchar_t *_Filename, struct stat *_Stat) __MINGW_ASM_CALL(wstat64i32);
+#endif
+#endif
+#endif
+
+struct 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;
+  __MINGW_EXTENSION __int64 st_size;
+  __time64_t st_atime;
+  __time64_t st_mtime;
+  __time64_t st_ctime;
+};
+int __cdecl fstat64(int _Desc, struct stat64 *_Stat);
+int __cdecl stat64(const char *_Filename, struct stat64 *_Stat);
+int __cdecl wstat64(const wchar_t *_Filename, struct stat64 *_Stat);
+
 #endif
 
 #ifdef __cplusplus
-- 
2.20.1


>From 9b4920eab24d82f88847949327972a4d0a60ad17 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.ro...@gmail.com>
Date: Thu, 6 Mar 2025 23:26:57 +0100
Subject: [PATCH 09/10] crt: Provide emulation of _time64 function

Function _time64 is avaulable since msvcr70.dll. For older msvcrt versions
provide emulation via WinAPI GetSystemTime() function which is available on
all Windows versions and returns native value in SYSTEMTIME format.
---
 mingw-w64-crt/Makefile.am              |  2 ++
 mingw-w64-crt/lib-common/msvcrt.def.in |  2 +-
 mingw-w64-crt/misc/_time64.c           | 31 ++++++++++++++++++++++++++
 3 files changed, 34 insertions(+), 1 deletion(-)
 create mode 100644 mingw-w64-crt/misc/_time64.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 3f75c8958b71..fadac4c61b0e 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -512,6 +512,7 @@ src_msvcrt32=\
   misc/_free_locale.c \
   misc/_get_current_locale.c \
   misc/_initterm_e.c \
+  misc/_time64.c \
   misc/btowc.c \
   misc/imaxabs.c \
   misc/lc_locale_func.c \
@@ -786,6 +787,7 @@ src_pre_msvcr70=\
   misc/___mb_cur_max_func.c \
   misc/__pctype_func.c \
   misc/__pwctype_func.c \
+  misc/_time64.c \
   misc/lc_locale_func.c \
   misc/strtoimax.c \
   misc/strtoumax.c \
diff --git a/mingw-w64-crt/lib-common/msvcrt.def.in b/mingw-w64-crt/lib-common/msvcrt.def.in
index fe1c8164234c..9204c53526bb 100644
--- a/mingw-w64-crt/lib-common/msvcrt.def.in
+++ b/mingw-w64-crt/lib-common/msvcrt.def.in
@@ -1161,7 +1161,7 @@ _localtime64
 _mktime64
 F_X86_ANY(_osplatform DATA)
 F_NON_I386(_stat64) ; i386 _stat64 replaced by emu
-_time64
+F_NON_I386(_time64) ; i386 _time64 replaced by emu
 _utime64
 _wctime64
 _wfindfirst64
diff --git a/mingw-w64-crt/misc/_time64.c b/mingw-w64-crt/misc/_time64.c
new file mode 100644
index 000000000000..0eb9a9988a2d
--- /dev/null
+++ b/mingw-w64-crt/misc/_time64.c
@@ -0,0 +1,31 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include <windows.h>
+#include <time.h>
+
+#include "filetime_to_time64.h"
+
+static __time64_t __cdecl emu__time64(__time64_t *timeptr)
+{
+    SYSTEMTIME systemtime;
+    FILETIME filetime;
+    __time64_t time64;
+    GetSystemTime(&systemtime);
+    if (SystemTimeToFileTime(&systemtime, &filetime))
+        time64 = filetime_to_time64(&filetime);
+    else
+        time64 = -1;
+    if (timeptr)
+        *timeptr = time64;
+    return time64;
+}
+
+#define RETT __time64_t
+#define FUNC _time64
+#define ARGS __time64_t *timeptr
+#define CALL timeptr
+#include "msvcrt_or_emu_glue.h"
-- 
2.20.1


>From 0ac97fa5c26de5dad8f3993ac826ffbe3ba7f69a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.ro...@gmail.com>
Date: Wed, 1 May 2024 02:28:14 +0200
Subject: [PATCH 10/10] headers: Add support for -D_TIME_BITS=64

Currently all 32-bit non-UCRT builds are forced to use time_t type as
32-bit and also all time_t functions in 32-bit form.

With this change, any msvcrt.dll based 32-bit application can use 64-bit
time_t functions by defining -D_TIME_BITS=64 flag during compilation.

Flag -D_TIME_BITS=64 is recognized also by GNU C library header files for
the same purpose.

When both _USE_32BIT_TIME_T and _TIME_BITS are defined by application then
_USE_32BIT_TIME_T override effect of _TIME_BITS.
---
 mingw-w64-headers/crt/_mingw.h.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mingw-w64-headers/crt/_mingw.h.in b/mingw-w64-headers/crt/_mingw.h.in
index 0752993f31d2..8e5944a8374f 100644
--- a/mingw-w64-headers/crt/_mingw.h.in
+++ b/mingw-w64-headers/crt/_mingw.h.in
@@ -384,7 +384,7 @@ typedef int __int128 __attribute__ ((__mode__ (TI)));
 
 #ifndef __WIDL__
 
-#if defined (_WIN32) && !defined (_WIN64) && !defined (__MINGW_USE_VC2005_COMPAT) && !defined (_UCRT)
+#if defined (_WIN32) && !defined (_WIN64) && !defined (__MINGW_USE_VC2005_COMPAT) && !defined (_UCRT) && !(defined (_TIME_BITS) && _TIME_BITS == 64)
 #ifndef _USE_32BIT_TIME_T
 #define _USE_32BIT_TIME_T
 #endif
-- 
2.20.1

_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to