crt: Fix fstat() to signal overflow and return correct type for directories

msvcrt _fstat function is broken and for directory fd it returns S_ISREG
type. 32-bit pre-msvcr110 _fstat does not properly signal size overflow.
UCRT seems to work correctly.

Add mingw-w64 fstat wrappers around msvcrt _fstat functions which change
S_IFMT to S_IFDIR if winapi filehandle has FILE_ATTRIBUTE_DIRECTORY
attribute set. Provide wrapper for all 4 fstat size_t/time_t variants.
For 32-bit pre-msvcr110 builds, adds similar workaround like for existing
mingw-w64 stat functions.

Currently our mingw-w64 header files defines non-underscore fstat and
fstat64 functions as __MINGW_ASM_CALL redirects to underscore _fstat32,
_fstat32i64, _fstat64i32, _fstat64 functions based on _FILE_OFFSET_BITS and
_TIME_BITS macros. So every one of these 4 functions needs its own fixup
wrapper. This change introduce 4 new non-underscore symbols fstat32,
fstat32i64, fstat64i32, fstat64 which provides necessary fixup and to which
mingw-w64 sys/stat.h header file redirects existing fstat and fstat64
functions. UCRT does not need any fixup, so new symbols are added as
aliases in def files.

Co-authored-by: LIU Hao <lh_mouse@126.com>
Signed-off-by: LIU Hao <lh_mouse@126.com>
diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index e10cfe1..c266b33 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -190,6 +190,8 @@
   stdio/_strtof_l.c \
   stdio/_wcstof_l.c \
   stdio/acrt_iob_func.c \
+  stdio/fstat32i64.c \
+  stdio/fstat64.c \
   stdio/strtof.c \
   stdio/snprintf_alias.c \
   stdio/snwprintf_alias.c \
@@ -374,6 +376,8 @@
   stdio/_getc_nolock.c \
   stdio/mingw_lock.c \
   stdio/msvcrtos_ftruncate64.c \
+  stdio/msvcr110pre_fstat32.c \
+  stdio/msvcr110pre_fstat64i32.c \
   stdio/msvcr110pre_stat32.c \
   stdio/msvcr110pre_stat64i32.c \
   stdio/msvcr110pre_wstat32.c \
@@ -1028,6 +1032,8 @@
   misc/imaxdiv.c
 
 src_pre_msvcr110=\
+  stdio/msvcr110pre_fstat32.c \
+  stdio/msvcr110pre_fstat64i32.c \
   stdio/msvcr110pre_stat32.c \
   stdio/msvcr110pre_stat64i32.c \
   stdio/msvcr110pre_wstat32.c \
@@ -1060,6 +1066,8 @@
   misc/__p__osver_emul.c
 
 src_post_msvcr100=\
+  stdio/msvcr110plus_fstat32.c \
+  stdio/msvcr110plus_fstat64i32.c \
   stdio/msvcr110plus_stat32.c \
   stdio/msvcr110plus_stat64i32.c \
   stdio/msvcr110plus_wstat32.c \
@@ -1294,7 +1302,7 @@
   stdio/_Exit.c \
   stdio/asprintf.c \
   stdio/mingw_ftruncate64.c      stdio/lltoa.c             stdio/lltow.c \
-  stdio/__mingw_fix_stat.h stdio/__mingw_fix_stat_finish.c \
+  stdio/__mingw_fix_stat.h stdio/__mingw_fix_stat_finish.c stdio/__mingw_fix_fstat_finish.c \
   stdio/__mingw_fix_stat_path.c stdio/__mingw_fix_wstat_path.c \
   \
   stdio/mingw_pformat.h    stdio/mingw_sformat.h     stdio/mingw_swformat.h \
diff --git a/mingw-w64-crt/def-include/crt-aliases.def.in b/mingw-w64-crt/def-include/crt-aliases.def.in
index 8f9ee2e..2b88b69 100644
--- a/mingw-w64-crt/def-include/crt-aliases.def.in
+++ b/mingw-w64-crt/def-include/crt-aliases.def.in
@@ -64,13 +64,11 @@
 ADD_UNDERSCORE(fileno)
 ; ADD_UNDERSCORE(flushall)
 ADD_UNDERSCORE(fputchar)
-#ifdef FIXED_SIZE_SYMBOLS
-#ifndef CRTDLL
-ADD_UNDERSCORE(fstat)
-#endif
-#else
+#if defined(UCRTBASE)
 F32(fstat == _fstat32)
 F64(fstat == _fstat64i32)
+#else
+; fstat for non-UCRT is provided by mingw to workaround S_IFDIR issue in _fstat
 #endif
 ; ftime is provided in misc/ftime32.c or misc/ftime64.c as MS _ftime is not ABI compatible with POSIX ftime
 #if defined(UCRTBASE)
@@ -305,23 +303,23 @@
 lseek64 == _lseeki64
 #endif
 #ifdef UCRTBASE
+fstat32 == _fstat32
+fstat32i64 == _fstat32i64
+fstat64 == _fstat64
+fstat64i32 == _fstat64i32
 stat32 == _stat32
 stat32i64 == _stat32i64
 stat64 == _stat64
 stat64i32 == _stat64i32
 #else
+; fstat for non-UCRT is provided by mingw to workaround S_IFDIR issue in _fstat
 ; stat for non-UCRT is provided by mingw to workaround trailing slash issue in _stat
 #endif
 #ifdef FIXED_SIZE_SYMBOLS
-// NO_FIXED_SIZE_64_ALIAS means that DLL provides the native _fstat64 symbol
-#if defined(NO_FIXED_SIZE_64_ALIAS) && !defined(NO_FSTAT64_ALIAS)
-fstat64 == _fstat64
-#endif
 #ifdef WITH_FSEEKO64_ALIAS
 fseeko64 == _fseeki64
 #endif
 #else
-fstat64 == _fstat64
 fseeko64 == _fseeki64
 ftello64 == _ftelli64
 #endif
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 a0665a3..bdaa9bc 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
@@ -37,10 +37,13 @@
 F32(_fstati64 == _fstat32i64)
 F64(_fstati64 == _fstat64)
 _fstat32
+fstat32 == _fstat32
 _fstat32i64
+fstat32i64 == _fstat32i64
 _fstat64
 fstat64 == _fstat64
 _fstat64i32
+fstat64i32 == _fstat64i32
 _fullpath
 _getdiskfree
 _getdrive
diff --git a/mingw-w64-crt/lib-common/msvcrt.def.in b/mingw-w64-crt/lib-common/msvcrt.def.in
index 500d076..0a76606 100644
--- a/mingw-w64-crt/lib-common/msvcrt.def.in
+++ b/mingw-w64-crt/lib-common/msvcrt.def.in
@@ -1907,10 +1907,6 @@
 ; This includes list of some symbol alises for compatibility with C99 and POSIX functions and symbols from other msvcr* libraries
 #define FIXED_SIZE_SYMBOLS
 #define NO_FIXED_SIZE_64_ALIAS
-#ifdef __i386__
-; i386 fstat64 alias provided by emu
-#define NO_FSTAT64_ALIAS
-#endif
 #define NO_TIME_ALIAS
 #define NO_TMPFILE_ALIAS
 #define NO_STRCMPI_ALIAS
diff --git a/mingw-w64-crt/misc/crtdll_fstat.c b/mingw-w64-crt/misc/crtdll_fstat.c
index 2fe5b1e..5441e32 100644
--- a/mingw-w64-crt/misc/crtdll_fstat.c
+++ b/mingw-w64-crt/misc/crtdll_fstat.c
@@ -22,8 +22,3 @@
 #undef _fstat
 int __attribute__ ((alias ("_fstat32"))) __cdecl _fstat(int fd, struct _stat32 *stat);
 extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(_fstat32))))) (__cdecl *__MINGW_IMP_SYMBOL(_fstat))(int fd, struct _stat32 *stat);
-
-#undef fstat
-struct stat;
-int __attribute__ ((alias ("_fstat32"))) __cdecl fstat(int fd, struct stat *stat);
-extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(_fstat32))))) (__cdecl *__MINGW_IMP_SYMBOL(fstat))(int fd, struct stat *stat);
diff --git a/mingw-w64-crt/stdio/__mingw_fix_fstat_finish.c b/mingw-w64-crt/stdio/__mingw_fix_fstat_finish.c
new file mode 100644
index 0000000..724b960
--- /dev/null
+++ b/mingw-w64-crt/stdio/__mingw_fix_fstat_finish.c
@@ -0,0 +1,18 @@
+/**
+ * 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 <sys/stat.h>
+#include <windows.h>
+#include "__mingw_fix_stat.h"
+
+int __mingw_fix_fstat_finish(int ret, int fd, unsigned short *mode)
+{
+  /* msvcrt's _fstat fills S_IFREG for directories. Fix it to S_IFDIR. */
+  BY_HANDLE_FILE_INFORMATION fi;
+  if (ret == 0 && S_ISREG(*mode) && GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &fi) && (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+    *mode = (*mode & ~S_IFMT) | S_IFDIR;
+  return ret;
+}
diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat.h b/mingw-w64-crt/stdio/__mingw_fix_stat.h
index 9096062..1317dbf 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_stat.h
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat.h
@@ -11,5 +11,12 @@
 wchar_t* __mingw_fix_wstat_path (const wchar_t* _path);
 int __mingw_fix_stat_finish(int ret, const void *orig_path, void *used_path,
                             unsigned short mode);
+int __mingw_fix_fstat_finish(int ret, int fd, unsigned short *mode);
+
+#define __MINGW_FIXED_FSTAT(fstat_func, fd, obj) ({ \
+   int _fstat_ret = fstat_func(fd, obj); \
+   _fstat_ret = __mingw_fix_fstat_finish(_fstat_ret, fd, &(obj)->st_mode); \
+   _fstat_ret; \
+})
 
 #endif
diff --git a/mingw-w64-crt/stdio/_fstat64.c b/mingw-w64-crt/stdio/_fstat64.c
index 28c5d73..9386dd9 100644
--- a/mingw-w64-crt/stdio/_fstat64.c
+++ b/mingw-w64-crt/stdio/_fstat64.c
@@ -43,6 +43,3 @@
 #define ARGS int fd, struct _stat64 *stat
 #define CALL fd, stat
 #include "msvcrt_or_emu_glue.h"
-
-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/fstat32i64.c b/mingw-w64-crt/stdio/fstat32i64.c
new file mode 100644
index 0000000..8ba257d
--- /dev/null
+++ b/mingw-w64-crt/stdio/fstat32i64.c
@@ -0,0 +1,16 @@
+/**
+ * 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 <sys/stat.h>
+#include <stdlib.h>
+#include "__mingw_fix_stat.h"
+
+int __cdecl fstat32i64(int fd, struct _stat32i64 *stat);
+int __cdecl fstat32i64(int fd, struct _stat32i64 *stat)
+{
+  return __MINGW_FIXED_FSTAT(_fstat32i64, fd, stat);
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(fstat32i64))(int, struct _stat32i64 *) = fstat32i64;
diff --git a/mingw-w64-crt/stdio/fstat64.c b/mingw-w64-crt/stdio/fstat64.c
new file mode 100644
index 0000000..442409c
--- /dev/null
+++ b/mingw-w64-crt/stdio/fstat64.c
@@ -0,0 +1,15 @@
+/**
+ * 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 <sys/stat.h>
+#include <stdlib.h>
+#include "__mingw_fix_stat.h"
+
+int __cdecl fstat64(int fd, struct stat64 *stat)
+{
+  return __MINGW_FIXED_FSTAT(_fstat64, fd, (struct _stat64 *)stat);
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(fstat64))(int, struct stat64 *) = fstat64;
diff --git a/mingw-w64-crt/stdio/msvcr110plus_fstat32.c b/mingw-w64-crt/stdio/msvcr110plus_fstat32.c
new file mode 100644
index 0000000..dfc57b2
--- /dev/null
+++ b/mingw-w64-crt/stdio/msvcr110plus_fstat32.c
@@ -0,0 +1,24 @@
+/**
+ * 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 <sys/stat.h>
+#include <stdlib.h>
+#include "__mingw_fix_stat.h"
+
+int __cdecl fstat32(int fd, struct _stat32 *stat);
+int __cdecl fstat32(int fd, struct _stat32 *stat)
+{
+  return __MINGW_FIXED_FSTAT(_fstat32, fd, stat);
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(fstat32))(int, struct _stat32 *) = fstat32;
+
+/* On 32-bit systems fstat() is ABI-compatible with fstat32() */
+#ifndef _WIN64
+#undef stat
+struct stat;
+int __attribute__ ((alias ("fstat32"))) __cdecl fstat(int fd, struct stat *stat);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(fstat32))))) (__cdecl *__MINGW_IMP_SYMBOL(fstat))(int fd, struct stat *stat);
+#endif
diff --git a/mingw-w64-crt/stdio/msvcr110plus_fstat64i32.c b/mingw-w64-crt/stdio/msvcr110plus_fstat64i32.c
new file mode 100644
index 0000000..7b14828
--- /dev/null
+++ b/mingw-w64-crt/stdio/msvcr110plus_fstat64i32.c
@@ -0,0 +1,24 @@
+/**
+ * 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 <sys/stat.h>
+#include <stdlib.h>
+#include "__mingw_fix_stat.h"
+
+int __cdecl fstat64i32(int fd, struct _stat64i32 *stat);
+int __cdecl fstat64i32(int fd, struct _stat64i32 *stat)
+{
+  return __MINGW_FIXED_FSTAT(_fstat64i32, fd, stat);
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(fstat64i32))(int fd, struct _stat64i32 *) = fstat64i32;
+
+/* On 64-bit systems fstat() is ABI-compatible with fstat64i32() */
+#ifdef _WIN64
+#undef stat
+struct stat;
+int __attribute__ ((alias ("fstat64i32"))) __cdecl fstat(int fd, struct stat *stat);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(fstat64i32))))) (__cdecl *__MINGW_IMP_SYMBOL(fstat))(int fd, struct stat *stat);
+#endif
diff --git a/mingw-w64-crt/stdio/msvcr110plus_stat32.c b/mingw-w64-crt/stdio/msvcr110plus_stat32.c
index c31527f..f936ef9 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_stat32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_stat32.c
@@ -19,7 +19,7 @@
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat32))(const char *, struct _stat32 *) = stat32;
 
-/* On 32-bit systems is stat() function ABI compatible with stat32() function */
+/* On 32-bit systems stat() is ABI-compatible with stat32() */
 #ifndef _WIN64
 #undef stat
 struct stat;
diff --git a/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c b/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c
index 7df90a0..89c3e05 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c
@@ -19,7 +19,7 @@
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat64i32))(const char *, struct _stat64i32 *) = stat64i32;
 
-/* On 64-bit systems is stat() function ABI compatible with stat64i32() function */
+/* On 64-bit systems stat() is ABI-compatible with stat64i32() */
 #ifdef _WIN64
 #undef stat
 struct stat;
diff --git a/mingw-w64-crt/stdio/msvcr110plus_wstat32.c b/mingw-w64-crt/stdio/msvcr110plus_wstat32.c
index 15f55cd..50e183c 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_wstat32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_wstat32.c
@@ -19,7 +19,7 @@
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat32))(const wchar_t *, struct _stat32 *) = wstat32;
 
-/* On 32-bit systems is wstat() function ABI compatible with wstat32() function */
+/* On 32-bit systems wstat() is ABI-compatible with wstat32() */
 #ifndef _WIN64
 #undef stat
 #undef wstat
diff --git a/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c b/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c
index c2f1e32..caf5ca7 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c
@@ -19,7 +19,7 @@
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat64i32))(const wchar_t *, struct _stat64i32 *) = wstat64i32;
 
-/* On 64-bit systems is wstat() function ABI compatible with wstat64i32() function */
+/* On 64-bit systems wstat() is ABI-compatible with wstat64i32() */
 #ifdef _WIN64
 #undef stat
 #undef wstat
diff --git a/mingw-w64-crt/stdio/msvcr110pre_fstat32.c b/mingw-w64-crt/stdio/msvcr110pre_fstat32.c
new file mode 100644
index 0000000..cbc6dbb
--- /dev/null
+++ b/mingw-w64-crt/stdio/msvcr110pre_fstat32.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.
+ */
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include "__mingw_fix_stat.h"
+
+/* For pre-msvcr110 builds, we cannot use _fstat32() as it does
+ * not signal EOVERFLOW when file size does not fit into the st_size field,
+ * as it is required by POSIX fstat().
+ * This file is used only for pre-msvcr110 builds.
+ */
+int __cdecl fstat32(int fd, struct _stat32 *stat);
+int __cdecl fstat32(int fd, struct _stat32 *stat)
+{
+  struct _stat32i64 st;
+  int ret = __MINGW_FIXED_FSTAT(_fstat32i64, fd, &st);
+  if (ret != 0)
+    return ret;
+  if (st.st_size > INT32_MAX) {
+    errno = EOVERFLOW;
+    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 0;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(fstat32))(int, struct _stat32 *) = fstat32;
+
+/* On 32-bit systems fstat() is ABI-compatible with fstat32() */
+#ifndef _WIN64
+#undef stat
+struct stat;
+int __attribute__ ((alias ("fstat32"))) __cdecl fstat(int fd, struct stat *stat);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(fstat32))))) (__cdecl *__MINGW_IMP_SYMBOL(fstat))(int fd, struct stat *stat);
+#endif
diff --git a/mingw-w64-crt/stdio/msvcr110pre_fstat64i32.c b/mingw-w64-crt/stdio/msvcr110pre_fstat64i32.c
new file mode 100644
index 0000000..6ca74be
--- /dev/null
+++ b/mingw-w64-crt/stdio/msvcr110pre_fstat64i32.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.
+ */
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include "__mingw_fix_stat.h"
+
+/* For pre-msvcr110 builds, we cannot use _fstat64i32() as it does
+ * not signal EOVERFLOW when file size does not fit into the st_size field,
+ * as it is required by POSIX fstat().
+ * This file is used only for pre-msvcr110 builds.
+ */
+int __cdecl fstat64i32(int fd, struct _stat64i32 *stat);
+int __cdecl fstat64i32(int fd, struct _stat64i32 *stat)
+{
+  struct _stat64 st;
+  int ret = __MINGW_FIXED_FSTAT(_fstat64, fd, &st);
+  if (ret != 0)
+    return ret;
+  if (st.st_size > INT32_MAX) {
+    errno = EOVERFLOW;
+    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 0;
+}
+int (__cdecl *__MINGW_IMP_SYMBOL(fstat64i32))(int, struct _stat64i32 *) = fstat64i32;
+
+/* On 64-bit systems stat() is ABI-compatible with stat64i32() */
+#ifdef _WIN64
+#undef stat
+struct stat;
+int __attribute__ ((alias ("fstat64i32"))) __cdecl fstat(int fd, struct stat *stat);
+extern int __attribute__ ((alias (__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(fstat64i32))))) (__cdecl *__MINGW_IMP_SYMBOL(fstat))(int fd, struct stat *stat);
+#endif
diff --git a/mingw-w64-crt/stdio/msvcr110pre_stat32.c b/mingw-w64-crt/stdio/msvcr110pre_stat32.c
index 0d58f7b..196c2de 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_stat32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_stat32.c
@@ -10,9 +10,9 @@
 #include <errno.h>
 #include "__mingw_fix_stat.h"
 
-/* For pre-msvcr110 builds, we cannot use _stat32() function as it does
+/* For pre-msvcr110 builds, we cannot use _stat32() as it does
  * not signal EOVERFLOW when file size does not fit into the st_size field,
- * as it is required by POSIX stat() function.
+ * as it is required by POSIX stat().
  * This file is used only for pre-msvcr110 builds.
  */
 int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat);
@@ -26,7 +26,7 @@
   ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode);
   if (ret != 0)
     return ret;
-  if (st.st_size > UINT32_MAX) {
+  if (st.st_size > INT32_MAX) {
     errno = EOVERFLOW;
     return -1;
   }
@@ -45,7 +45,7 @@
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat32))(const char *, struct _stat32 *) = stat32;
 
-/* On 32-bit systems is stat() function ABI compatible with stat32() function */
+/* On 32-bit systems stat() is ABI-compatible with stat32() */
 #ifndef _WIN64
 #undef stat
 struct stat;
diff --git a/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c b/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c
index b2b037a..2a6cc9a 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c
@@ -10,9 +10,9 @@
 #include <errno.h>
 #include "__mingw_fix_stat.h"
 
-/* For pre-msvcr110 builds, we cannot use _stat64i32() function as it does
+/* For pre-msvcr110 builds, we cannot use _stat64i32() as it does
  * not signal EOVERFLOW when file size does not fit into the st_size field,
- * as it is required by POSIX stat() function.
+ * as it is required by POSIX stat().
  * This file is used only for pre-msvcr110 builds.
  */
 int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat);
@@ -26,7 +26,7 @@
   ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode);
   if (ret != 0)
     return ret;
-  if (st.st_size > UINT32_MAX) {
+  if (st.st_size > INT32_MAX) {
     errno = EOVERFLOW;
     return -1;
   }
@@ -45,7 +45,7 @@
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat64i32))(const char *, struct _stat64i32 *) = stat64i32;
 
-/* On 64-bit systems is stat() function ABI compatible with stat64i32() function */
+/* On 64-bit systems stat() is ABI-compatible with stat64i32() */
 #ifdef _WIN64
 #undef stat
 struct stat;
diff --git a/mingw-w64-crt/stdio/msvcr110pre_wstat32.c b/mingw-w64-crt/stdio/msvcr110pre_wstat32.c
index b4af68e..b9848ff 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_wstat32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_wstat32.c
@@ -10,9 +10,9 @@
 #include <errno.h>
 #include "__mingw_fix_stat.h"
 
-/* For pre-msvcr110 builds, we cannot use _wstat32() function as it does
+/* For pre-msvcr110 builds, we cannot use _wstat32() as it does
  * not signal EOVERFLOW when file size does not fit into the st_size field,
- * as it is required by POSIX stat() function.
+ * as it is required by POSIX stat().
  * This file is used only for pre-msvcr110 builds.
  */
 int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat);
@@ -26,7 +26,7 @@
   ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode);
   if (ret != 0)
     return ret;
-  if (st.st_size > UINT32_MAX) {
+  if (st.st_size > INT32_MAX) {
     errno = EOVERFLOW;
     return -1;
   }
@@ -45,7 +45,7 @@
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat32))(const wchar_t *, struct _stat32 *) = wstat32;
 
-/* On 32-bit systems is wstat() function ABI compatible with wstat32() function */
+/* On 32-bit systems wstat() is ABI-compatible with wstat32() */
 #ifndef _WIN64
 #undef stat
 #undef wstat
diff --git a/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c b/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c
index 9a55612..a7611bf 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c
@@ -10,9 +10,9 @@
 #include <errno.h>
 #include "__mingw_fix_stat.h"
 
-/* For pre-msvcr110 builds, we cannot use _wstat64i32() function as it does
+/* For pre-msvcr110 builds, we cannot use _wstat64i32() as it does
  * not signal EOVERFLOW when file size does not fit into the st_size field,
- * as it is required by POSIX stat() function.
+ * as it is required by POSIX stat().
  * This file is used only for pre-msvcr110 builds.
  */
 int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat);
@@ -26,7 +26,7 @@
   ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode);
   if (ret != 0)
     return ret;
-  if (st.st_size > UINT32_MAX) {
+  if (st.st_size > INT32_MAX) {
     errno = EOVERFLOW;
     return -1;
   }
@@ -45,7 +45,7 @@
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat64i32))(const wchar_t *, struct _stat64i32 *) = wstat64i32;
 
-/* On 64-bit systems is wstat() function ABI compatible with wstat64i32() function */
+/* On 64-bit systems wstat() is ABI-compatible with wstat64i32() */
 #ifdef _WIN64
 #undef stat
 #undef wstat
diff --git a/mingw-w64-crt/testcases/t_fstat.c b/mingw-w64-crt/testcases/t_fstat.c
index 0e85bd5..0622b9e 100644
--- a/mingw-w64-crt/testcases/t_fstat.c
+++ b/mingw-w64-crt/testcases/t_fstat.c
@@ -1,15 +1,53 @@
 #include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <windows.h>
 
-int main(int argc, char *argv[])
+#define TMPTMPL "mingw-w64-fstat-XXXXXX"
+static char dirpath[MAX_PATH + sizeof(TMPTMPL)];
+
+static void remove_dirpath(void) { rmdir(dirpath); }
+
+int main()
 {
     struct stat st;
     struct stat64 st64;
-    if (0 == fstat(0, &st))
-        printf("mode = %x\n", st.st_mode);
-    if (0 == fstat64(0, &st64))
-        printf("mode = %x\n", st64.st_mode);
+    HANDLE handle;
+    int dirfd;
+
+    assert(fstat(0, &st) == 0);
+    printf("fstat(0): mode = %08o\n", st.st_mode);
+    assert(!S_ISDIR(st.st_mode));
+
+    assert(fstat64(0, &st64) == 0);
+    printf("fstat64(0): mode = %08o\n", st64.st_mode);
+    assert(!S_ISDIR(st64.st_mode));
+
+    assert(GetTempPathA(MAX_PATH, dirpath));
+    printf("GetTempPathA(): path=%s\n", dirpath);
+
+    strcat(dirpath, TMPTMPL);
+    assert(mkdtemp(dirpath));
+    atexit(remove_dirpath);
+    printf("mkdtemp(): path=%s\n", dirpath);
+
+    handle = CreateFileA(dirpath, FILE_READ_ATTRIBUTES | DELETE, FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, NULL);
+    assert(handle != INVALID_HANDLE_VALUE);
+
+    dirfd = _open_osfhandle((intptr_t)handle, O_RDONLY);
+    assert(dirfd >= 0);
+
+    assert(fstat(dirfd, &st) == 0);
+    printf("fstat(dirfd): mode = %08o\n", st.st_mode);
+    assert(S_ISDIR(st.st_mode));
+
+    assert(fstat64(dirfd, &st64) == 0);
+    printf("fstat64(dirfd): mode = %08o\n", st64.st_mode);
+    assert(S_ISDIR(st64.st_mode));
+
     return 0;
 }
 
diff --git a/mingw-w64-headers/crt/sys/stat.h b/mingw-w64-headers/crt/sys/stat.h
index 5d2e6df..347f66e 100644
--- a/mingw-w64-headers/crt/sys/stat.h
+++ b/mingw-w64-headers/crt/sys/stat.h
@@ -149,21 +149,21 @@
 };
 #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
 #ifdef _USE_32BIT_TIME_T
-int __cdecl fstat(int _Desc, struct stat *_Stat) __MINGW_ASM_CALL(_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
-int __cdecl fstat(int _Desc, struct stat *_Stat) __MINGW_ASM_CALL(_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 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 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