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 in all pre-msvcr100 CRT libraries provides only truncated 32-bit
file size and invalid value -1 as timestamp.

The real 64-bit non-truncated file size and valid timestamp information is
retrieved via the WinAPI calls.

For _stat64() and _wstat64() use WinAPI FindFirstFile() call. And for
_fstat64() use WinAPI GetFileInformationByHandle() call. Same thing is
doing msvcrt DLL library for other stat functions. Note that both WinAPI
functions are available in all Windows version (since the Windows NT 3.1
and the Windows 95).

Functions _fstat64() and _stat64() are already used by mingw-w64 crt, so
ensure that they are present in every CRT import library.

Signed-off-by: Martin Storsjö <martin@martin.st>
diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 04b61c4..32187b8 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -549,11 +549,14 @@
   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
@@ -815,10 +818,13 @@
   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
@@ -4279,6 +4285,7 @@
   $(top_srcdir)/lib64/*.def.in \
   $(top_srcdir)/libarm32/*.def.in \
   $(top_srcdir)/def-include/*.def.in \
+  include/filetime_to_time64.h \
   crt/binmode.c \
   crt/crtbegin.c \
   crt/crtdll.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 0000000..2d84598
--- /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 681b491..2df0c02 100644
--- a/mingw-w64-crt/lib-common/msvcrt.def.in
+++ b/mingw-w64-crt/lib-common/msvcrt.def.in
@@ -1153,20 +1153,20 @@
 _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 0000000..8df51a4
--- /dev/null
+++ b/mingw-w64-crt/stdio/_fstat64.c
@@ -0,0 +1,49 @@
+/**
+ * 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 <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; /* invalid value -1 */
+        stat->st_mtime = st.st_mtime; /* invalid value -1 */
+        stat->st_ctime = st.st_ctime; /* invalid value -1 */
+    }
+    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 0000000..2edab1e
--- /dev/null
+++ b/mingw-w64-crt/stdio/_stat64.c
@@ -0,0 +1,47 @@
+/**
+ * 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 "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; /* invalid value -1 */
+        stat->st_mtime = st.st_mtime; /* invalid value -1 */
+        stat->st_ctime = st.st_ctime; /* invalid value -1 */
+    }
+    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 0000000..1ffaf62
--- /dev/null
+++ b/mingw-w64-crt/stdio/_wstat64.c
@@ -0,0 +1,47 @@
+/**
+ * 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 "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; /* invalid value -1 */
+        stat->st_mtime = st.st_mtime; /* invalid value -1 */
+        stat->st_ctime = st.st_ctime; /* invalid value -1 */
+    }
+    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"