Hello, in attachment I'm sending a change which fixes mingw-w64 POSIX non-underscored stat() and wstat() function(s) (including large file and 64-bit time variants) for msvcrt.dll builds when calling stat on various Win32 device files (like "NUL") or when using "\\?\\\\" prefix. The MS underscored _stat / _wstat functions are untouched and stay as it.
Kirill run CI tests with this (and also other changes), tests passed: https://github.com/maiddaisuki/mingw-w64/actions/runs/22307392638 In my opinion MS underscored functions should do what MS implementation is doing, including all those MS bugs. If somebody needs POSIX behavior then should use POSIX functions which are without leading underscore. That is why underscored functions are untouched. Note that UCRT build does not have these issues and it is verified by automated test, part of change. So this change does not touch UCRT builds. This change address reported bugs: https://sourceforge.net/p/mingw-w64/bugs/1009/ https://github.com/mingw-w64/mingw-w64/issues/139 If you could contact AlttiRi (reporter of the second bug) then it would be nice as could try to test these changes and maybe provide more input as into bug tracker wrote more test cases.
>From 5fb75957039e477177770e75d23ebc42a3ae138f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]> Date: Sat, 13 Dec 2025 21:40:22 +0100 Subject: [PATCH 1/2] crt: Deduplicate mingw-w64 stat code into __MINGW_FIXED_STAT macro Create new function-like macro __MINGW_FIXED_STAT with same pattern as existing __MINGW_FIXED_FSTAT macro, and extract repeated stat and wstat code into that new __MINGW_FIXED_STAT macro. Use gcc compound statement extension to create function-like macro and __builtin_choose_expr to choose correct narrow or wide function based on the passed type (char* or wchar_t*). With this patterns, the whole stat and wstat code is deduplicated to macro functions. This allows to extend the __MINGW_FIXED_STAT code by followup changes without duplicating the functionality again. --- mingw-w64-crt/stdio/__mingw_fix_stat.h | 23 +++++++++++++++++++ mingw-w64-crt/stdio/msvcr110plus_stat32.c | 6 +---- mingw-w64-crt/stdio/msvcr110plus_stat64i32.c | 6 +---- mingw-w64-crt/stdio/msvcr110plus_wstat32.c | 6 +---- mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c | 6 +---- mingw-w64-crt/stdio/msvcr110pre_stat32.c | 6 +---- mingw-w64-crt/stdio/msvcr110pre_stat64i32.c | 6 +---- mingw-w64-crt/stdio/msvcr110pre_wstat32.c | 6 +---- mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c | 6 +---- mingw-w64-crt/stdio/stat32i64.c | 6 +---- mingw-w64-crt/stdio/stat64.c | 6 +---- mingw-w64-crt/stdio/wstat32i64.c | 6 +---- mingw-w64-crt/stdio/wstat64.c | 6 +---- 13 files changed, 35 insertions(+), 60 deletions(-) diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat.h b/mingw-w64-crt/stdio/__mingw_fix_stat.h index 1317dbffe2e8..13e4bf6d4403 100644 --- a/mingw-w64-crt/stdio/__mingw_fix_stat.h +++ b/mingw-w64-crt/stdio/__mingw_fix_stat.h @@ -19,4 +19,27 @@ int __mingw_fix_fstat_finish(int ret, int fd, unsigned short *mode); _fstat_ret; \ }) +#define __MINGW_CHOOSE_CHAR_WCHART_EXPR(var, char_expr, wchart_expr, other_expr) \ + __builtin_choose_expr(__builtin_types_compatible_p(typeof(var), char), char_expr, \ + __builtin_choose_expr(__builtin_types_compatible_p(typeof(var), wchar_t), wchart_expr, \ + other_expr)) + +#define __MINGW_PATH_PTR_TYPE(path) \ + typeof(__MINGW_CHOOSE_CHAR_WCHART_EXPR((path)[0], (char*)0, (wchar_t*)0, (void)0)) + +#define __MINGW_FIX_STAT_PATH(path) \ + __MINGW_CHOOSE_CHAR_WCHART_EXPR((path)[0], __mingw_fix_stat_path, __mingw_fix_wstat_path, NULL)(path) + +#define __MINGW_FIXED_STAT(stat_func, filename, obj) ({ \ + int _stat_ret; \ + __MINGW_PATH_PTR_TYPE(filename) path = __MINGW_FIX_STAT_PATH(filename); \ + if (path == NULL && (filename) != NULL) { \ + _stat_ret = -1; \ + } else { \ + _stat_ret = (stat_func)(path, (obj)); \ + _stat_ret = __mingw_fix_stat_finish(_stat_ret, (filename), path, (obj)->st_mode); \ + } \ + _stat_ret; \ +}) + #endif diff --git a/mingw-w64-crt/stdio/msvcr110plus_stat32.c b/mingw-w64-crt/stdio/msvcr110plus_stat32.c index f936ef9fc908..400ac1f29b75 100644 --- a/mingw-w64-crt/stdio/msvcr110plus_stat32.c +++ b/mingw-w64-crt/stdio/msvcr110plus_stat32.c @@ -11,11 +11,7 @@ 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); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _stat32(_path, _Stat); - return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode); + return __MINGW_FIXED_STAT(_stat32, _Filename, _Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(stat32))(const char *, struct _stat32 *) = stat32; diff --git a/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c b/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c index 89c3e050dda0..299abd8cb0c2 100644 --- a/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c +++ b/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c @@ -11,11 +11,7 @@ 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); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _stat64i32(_path, _Stat); - return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode); + return __MINGW_FIXED_STAT(_stat64i32, _Filename, _Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(stat64i32))(const char *, struct _stat64i32 *) = stat64i32; diff --git a/mingw-w64-crt/stdio/msvcr110plus_wstat32.c b/mingw-w64-crt/stdio/msvcr110plus_wstat32.c index 50e183cfb9ab..85291d93120b 100644 --- a/mingw-w64-crt/stdio/msvcr110plus_wstat32.c +++ b/mingw-w64-crt/stdio/msvcr110plus_wstat32.c @@ -11,11 +11,7 @@ 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); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _wstat32(_path, _Stat); - return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode); + return __MINGW_FIXED_STAT(_wstat32, _Filename, _Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(wstat32))(const wchar_t *, struct _stat32 *) = wstat32; diff --git a/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c b/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c index caf5ca79719a..baff6a09ea4f 100644 --- a/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c +++ b/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c @@ -11,11 +11,7 @@ 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); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _wstat64i32(_path, _Stat); - return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode); + return __MINGW_FIXED_STAT(_wstat64i32, _Filename, _Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(wstat64i32))(const wchar_t *, struct _stat64i32 *) = wstat64i32; diff --git a/mingw-w64-crt/stdio/msvcr110pre_stat32.c b/mingw-w64-crt/stdio/msvcr110pre_stat32.c index 196c2de4f0cb..8650db23df05 100644 --- a/mingw-w64-crt/stdio/msvcr110pre_stat32.c +++ b/mingw-w64-crt/stdio/msvcr110pre_stat32.c @@ -19,11 +19,7 @@ int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat); int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat) { struct _stat32i64 st; - char *_path = __mingw_fix_stat_path(_Filename); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _stat32i64(_path, &st); - ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode); + int ret = __MINGW_FIXED_STAT(_stat32i64, _Filename, &st); if (ret != 0) return ret; if (st.st_size > INT32_MAX) { diff --git a/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c b/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c index 2a6cc9afd79d..d929a4f09936 100644 --- a/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c +++ b/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c @@ -19,11 +19,7 @@ int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat); int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat) { struct _stat64 st; - char *_path = __mingw_fix_stat_path(_Filename); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _stat64(_path, &st); - ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode); + int ret = __MINGW_FIXED_STAT(_stat64, _Filename, &st); if (ret != 0) return ret; if (st.st_size > INT32_MAX) { diff --git a/mingw-w64-crt/stdio/msvcr110pre_wstat32.c b/mingw-w64-crt/stdio/msvcr110pre_wstat32.c index b9848ffff1d0..308c7f6446af 100644 --- a/mingw-w64-crt/stdio/msvcr110pre_wstat32.c +++ b/mingw-w64-crt/stdio/msvcr110pre_wstat32.c @@ -19,11 +19,7 @@ int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat); int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat) { struct _stat32i64 st; - wchar_t *_path = __mingw_fix_wstat_path(_Filename); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _wstat32i64(_path, &st); - ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode); + int ret = __MINGW_FIXED_STAT(_wstat32i64, _Filename, &st); if (ret != 0) return ret; if (st.st_size > INT32_MAX) { diff --git a/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c b/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c index a7611bfe0c11..f8ed493e71c8 100644 --- a/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c +++ b/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c @@ -19,11 +19,7 @@ int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat); int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat) { struct _stat64 st; - wchar_t *_path = __mingw_fix_wstat_path(_Filename); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _wstat64(_path, &st); - ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode); + int ret = __MINGW_FIXED_STAT(_wstat64, _Filename, &st); if (ret != 0) return ret; if (st.st_size > INT32_MAX) { diff --git a/mingw-w64-crt/stdio/stat32i64.c b/mingw-w64-crt/stdio/stat32i64.c index 0b0c5cde995b..f8e84bfd0ff9 100644 --- a/mingw-w64-crt/stdio/stat32i64.c +++ b/mingw-w64-crt/stdio/stat32i64.c @@ -11,10 +11,6 @@ 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); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _stat32i64(_path, _Stat); - return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode); + return __MINGW_FIXED_STAT(_stat32i64, _Filename, _Stat); } 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 index b1f5b430905b..0cef610b6dd0 100644 --- a/mingw-w64-crt/stdio/stat64.c +++ b/mingw-w64-crt/stdio/stat64.c @@ -10,10 +10,6 @@ int __cdecl stat64(const char *_Filename, struct stat64 *_Stat) { - char *_path = __mingw_fix_stat_path(_Filename); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _stat64(_path, (struct _stat64 *)_Stat); - return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode); + return __MINGW_FIXED_STAT(_stat64, _Filename, (struct _stat64 *)_Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(stat64))(const char *, struct stat64 *) = stat64; diff --git a/mingw-w64-crt/stdio/wstat32i64.c b/mingw-w64-crt/stdio/wstat32i64.c index 98dcaf11f595..d4bb87eaf2d3 100644 --- a/mingw-w64-crt/stdio/wstat32i64.c +++ b/mingw-w64-crt/stdio/wstat32i64.c @@ -11,10 +11,6 @@ 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); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _wstat32i64(_path, _Stat); - return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode); + return __MINGW_FIXED_STAT(_wstat32i64, _Filename, _Stat); } 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 index 76d54743500c..0f00a4ed628a 100644 --- a/mingw-w64-crt/stdio/wstat64.c +++ b/mingw-w64-crt/stdio/wstat64.c @@ -10,10 +10,6 @@ int __cdecl wstat64(const wchar_t *_Filename, struct stat64 *_Stat) { - wchar_t *_path = __mingw_fix_wstat_path(_Filename); - if (_path == NULL && _Filename != NULL) - return -1; - int ret = _wstat64(_path, (struct _stat64 *)_Stat); - return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode); + return __MINGW_FIXED_STAT(_wstat64, _Filename, (struct _stat64 *)_Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(wstat64))(const wchar_t *, struct stat64 *) = wstat64; -- 2.20.1 >From bfddd5b8ef5b48455deebda2908e67ae7137da07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]> Date: Sat, 13 Dec 2025 22:02:47 +0100 Subject: [PATCH 2/2] crt: Fix mingw-w64 stat implementation MS CRT _stat() implementation is known to be broken in different cases. mingw-w64 already contains fixup code for path processing, but this is not enough. For example _stat() returns incorrect type for Windows devices (like NUL) or reject \\?\ paths with ENOENT error. This is probably because WinAPI FindFirstFile function (which is in lot of times used as replacement for POSIX stat) does not return file type at all. mingw-w64 already provides working fstat() implementation (as wrapper around MS CRT _fstat()) which can be used as stat() fallback for already opened file handle. So if the MS CRT _stat() fails or returns that file type is regular file then try to open path in read-only mode and call mingw-w64 fstat() on it. Note that opening path can fail for many reasons (for example denied by ACL, denied by shared reservation or denied by delete_pending state) when regular FindFirstFile is working fine. So do not take errors from opening path as fatal error. Use the fstat just for correcting information returned by MS CRT _stat() function. Bug: https://sourceforge.net/p/mingw-w64/bugs/1009/ Bug: https://github.com/mingw-w64/mingw-w64/issues/139 --- mingw-w64-crt/Makefile.am | 1 + mingw-w64-crt/stdio/__mingw_fix_stat.h | 22 +++- .../stdio/__mingw_fix_stat_fallback_fd.c | 43 ++++++++ .../stdio/__mingw_fix_wstat_fallback_fd.c | 2 + mingw-w64-crt/stdio/msvcr110plus_stat32.c | 3 +- mingw-w64-crt/stdio/msvcr110plus_stat64i32.c | 3 +- mingw-w64-crt/stdio/msvcr110plus_wstat32.c | 3 +- mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c | 3 +- mingw-w64-crt/stdio/msvcr110pre_stat32.c | 3 +- mingw-w64-crt/stdio/msvcr110pre_stat64i32.c | 2 +- mingw-w64-crt/stdio/msvcr110pre_wstat32.c | 3 +- mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c | 2 +- mingw-w64-crt/stdio/stat32i64.c | 3 +- mingw-w64-crt/stdio/stat64.c | 2 +- mingw-w64-crt/stdio/wstat32i64.c | 3 +- mingw-w64-crt/stdio/wstat64.c | 2 +- mingw-w64-crt/testcases/t_stat.c | 103 +++++++++++++++++- 17 files changed, 189 insertions(+), 14 deletions(-) create mode 100644 mingw-w64-crt/stdio/__mingw_fix_stat_fallback_fd.c create mode 100644 mingw-w64-crt/stdio/__mingw_fix_wstat_fallback_fd.c diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am index c266b332cc36..a56c8790d64a 100644 --- a/mingw-w64-crt/Makefile.am +++ b/mingw-w64-crt/Makefile.am @@ -1304,6 +1304,7 @@ src_libmingwex=\ stdio/mingw_ftruncate64.c stdio/lltoa.c stdio/lltow.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_fix_stat_fallback_fd.c stdio/__mingw_fix_wstat_fallback_fd.c \ \ stdio/mingw_pformat.h stdio/mingw_sformat.h stdio/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/stdio/__mingw_fix_stat.h b/mingw-w64-crt/stdio/__mingw_fix_stat.h index 13e4bf6d4403..c6af57492ccd 100644 --- a/mingw-w64-crt/stdio/__mingw_fix_stat.h +++ b/mingw-w64-crt/stdio/__mingw_fix_stat.h @@ -12,6 +12,8 @@ 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); +int __mingw_fix_wstat_fallback_fd(int ret, const wchar_t *filename, unsigned short mode); +int __mingw_fix_stat_fallback_fd(int ret, const char *filename, unsigned short mode); #define __MINGW_FIXED_FSTAT(fstat_func, fd, obj) ({ \ int _fstat_ret = fstat_func(fd, obj); \ @@ -30,7 +32,17 @@ int __mingw_fix_fstat_finish(int ret, int fd, unsigned short *mode); #define __MINGW_FIX_STAT_PATH(path) \ __MINGW_CHOOSE_CHAR_WCHART_EXPR((path)[0], __mingw_fix_stat_path, __mingw_fix_wstat_path, NULL)(path) -#define __MINGW_FIXED_STAT(stat_func, filename, obj) ({ \ +#define __MINGW_FIX_STAT_FALLBACK_FD(ret, path, mode) \ + __MINGW_CHOOSE_CHAR_WCHART_EXPR((path)[0], __mingw_fix_stat_fallback_fd, __mingw_fix_wstat_fallback_fd, NULL)((ret), (path), (mode)) + +#define __MINGW_CREATE_FILE(path, ...) \ + __MINGW_CHOOSE_CHAR_WCHART_EXPR((path)[0], CreateFileA, CreateFileW, NULL)((path), ##__VA_ARGS__) + +#define __MINGW_STR_PBRK(path, accept) \ + __MINGW_CHOOSE_CHAR_WCHART_EXPR((path)[0], strpbrk, wcspbrk, NULL)((path), __MINGW_CHOOSE_CHAR_WCHART_EXPR((path)[0], accept, L##accept, NULL)) + +#define __MINGW_FIXED_STAT(fstat_func, stat_func, filename, obj) ({ \ + /* First call CRT _stat function with mingw path correction */ \ int _stat_ret; \ __MINGW_PATH_PTR_TYPE(filename) path = __MINGW_FIX_STAT_PATH(filename); \ if (path == NULL && (filename) != NULL) { \ @@ -39,6 +51,14 @@ int __mingw_fix_fstat_finish(int ret, int fd, unsigned short *mode); _stat_ret = (stat_func)(path, (obj)); \ _stat_ret = __mingw_fix_stat_finish(_stat_ret, (filename), path, (obj)->st_mode); \ } \ + /* If the CRT _stat function failed then fallback to mingw fstat function */ \ + int _stat_fd = __MINGW_FIX_STAT_FALLBACK_FD(_stat_ret, (filename), (obj)->st_mode); \ + if (_stat_fd >= 0) { \ + _stat_ret = fstat_func(_stat_fd, (void*)(obj)); \ + int _stat_errno = errno; \ + close(_stat_fd); \ + errno = _stat_errno; \ + } \ _stat_ret; \ }) diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat_fallback_fd.c b/mingw-w64-crt/stdio/__mingw_fix_stat_fallback_fd.c new file mode 100644 index 000000000000..5f0bf00157e6 --- /dev/null +++ b/mingw-w64-crt/stdio/__mingw_fix_stat_fallback_fd.c @@ -0,0 +1,43 @@ +/** + * 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 <string.h> +#include <wchar.h> +#include <errno.h> +#include <fcntl.h> +#include <windows.h> +#include "__mingw_fix_stat.h" + +#ifdef MINGW_FIX_STAT_IS_WIDE +int __mingw_fix_wstat_fallback_fd(int ret, const wchar_t *filename, unsigned short mode) +#else +int __mingw_fix_stat_fallback_fd(int ret, const char *filename, unsigned short mode) +#endif +{ + int fd = -1; + + /* + * CRT _stat does not handle paths with wildcard ? and * and returns ENOENT. + * CRT _stat incorrectly sets S_IFREG for pipe and char devices. + * For these cases open specified filename and return its fd which will be passed to CRT fstat() by caller. + */ + if ((ret < 0 && errno == ENOENT && __MINGW_STR_PBRK((filename), "?*")) || S_ISREG(mode)) { + HANDLE handle = __MINGW_CREATE_FILE((filename), FILE_READ_ATTRIBUTES, FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (handle != NULL && handle != INVALID_HANDLE_VALUE) { + /* Open filename and return id if CRT _stat failed or if the file is not regular disk file. */ + if (ret < 0 || GetFileType(handle) != FILE_TYPE_DISK) { + int saved_errno = errno; + fd = _open_osfhandle((intptr_t)handle, O_RDONLY); + errno = saved_errno; + if (fd < 0) + CloseHandle(handle); + } + } + } + + return fd; +} diff --git a/mingw-w64-crt/stdio/__mingw_fix_wstat_fallback_fd.c b/mingw-w64-crt/stdio/__mingw_fix_wstat_fallback_fd.c new file mode 100644 index 000000000000..1182ab55855d --- /dev/null +++ b/mingw-w64-crt/stdio/__mingw_fix_wstat_fallback_fd.c @@ -0,0 +1,2 @@ +#define MINGW_FIX_STAT_IS_WIDE +#include "__mingw_fix_stat_fallback_fd.c" diff --git a/mingw-w64-crt/stdio/msvcr110plus_stat32.c b/mingw-w64-crt/stdio/msvcr110plus_stat32.c index 400ac1f29b75..9042cea6a47a 100644 --- a/mingw-w64-crt/stdio/msvcr110plus_stat32.c +++ b/mingw-w64-crt/stdio/msvcr110plus_stat32.c @@ -8,10 +8,11 @@ #include <stdlib.h> #include "__mingw_fix_stat.h" +int __cdecl fstat32(int fd, struct _stat32 *stat); int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat); int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat) { - return __MINGW_FIXED_STAT(_stat32, _Filename, _Stat); + return __MINGW_FIXED_STAT(fstat32, _stat32, _Filename, _Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(stat32))(const char *, struct _stat32 *) = stat32; diff --git a/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c b/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c index 299abd8cb0c2..9ccd4621cbad 100644 --- a/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c +++ b/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c @@ -8,10 +8,11 @@ #include <stdlib.h> #include "__mingw_fix_stat.h" +int __cdecl fstat64i32(int fd, struct _stat64i32 *stat); int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat); int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat) { - return __MINGW_FIXED_STAT(_stat64i32, _Filename, _Stat); + return __MINGW_FIXED_STAT(fstat64i32, _stat64i32, _Filename, _Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(stat64i32))(const char *, struct _stat64i32 *) = stat64i32; diff --git a/mingw-w64-crt/stdio/msvcr110plus_wstat32.c b/mingw-w64-crt/stdio/msvcr110plus_wstat32.c index 85291d93120b..bbae9c249dfe 100644 --- a/mingw-w64-crt/stdio/msvcr110plus_wstat32.c +++ b/mingw-w64-crt/stdio/msvcr110plus_wstat32.c @@ -8,10 +8,11 @@ #include <stdlib.h> #include "__mingw_fix_stat.h" +int __cdecl fstat32(int fd, struct _stat32 *stat); int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat); int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat) { - return __MINGW_FIXED_STAT(_wstat32, _Filename, _Stat); + return __MINGW_FIXED_STAT(fstat32, _wstat32, _Filename, _Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(wstat32))(const wchar_t *, struct _stat32 *) = wstat32; diff --git a/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c b/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c index baff6a09ea4f..e0a37f495a94 100644 --- a/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c +++ b/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c @@ -8,10 +8,11 @@ #include <stdlib.h> #include "__mingw_fix_stat.h" +int __cdecl fstat64i32(int fd, struct _stat64i32 *stat); int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat); int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat) { - return __MINGW_FIXED_STAT(_wstat64i32, _Filename, _Stat); + return __MINGW_FIXED_STAT(fstat64i32, _wstat64i32, _Filename, _Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(wstat64i32))(const wchar_t *, struct _stat64i32 *) = wstat64i32; diff --git a/mingw-w64-crt/stdio/msvcr110pre_stat32.c b/mingw-w64-crt/stdio/msvcr110pre_stat32.c index 8650db23df05..296f7c3130a8 100644 --- a/mingw-w64-crt/stdio/msvcr110pre_stat32.c +++ b/mingw-w64-crt/stdio/msvcr110pre_stat32.c @@ -15,11 +15,12 @@ * as it is required by POSIX stat(). * This file is used only for pre-msvcr110 builds. */ +int __cdecl fstat32i64(int fd,struct _stat32i64 *stat); int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat); int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat) { struct _stat32i64 st; - int ret = __MINGW_FIXED_STAT(_stat32i64, _Filename, &st); + int ret = __MINGW_FIXED_STAT(fstat32i64, _stat32i64, _Filename, &st); if (ret != 0) return ret; if (st.st_size > INT32_MAX) { diff --git a/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c b/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c index d929a4f09936..7e2cdf77927b 100644 --- a/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c +++ b/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c @@ -19,7 +19,7 @@ int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat); int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat) { struct _stat64 st; - int ret = __MINGW_FIXED_STAT(_stat64, _Filename, &st); + int ret = __MINGW_FIXED_STAT(fstat64, _stat64, _Filename, &st); if (ret != 0) return ret; if (st.st_size > INT32_MAX) { diff --git a/mingw-w64-crt/stdio/msvcr110pre_wstat32.c b/mingw-w64-crt/stdio/msvcr110pre_wstat32.c index 308c7f6446af..db5886bccdf0 100644 --- a/mingw-w64-crt/stdio/msvcr110pre_wstat32.c +++ b/mingw-w64-crt/stdio/msvcr110pre_wstat32.c @@ -15,11 +15,12 @@ * as it is required by POSIX stat(). * This file is used only for pre-msvcr110 builds. */ +int __cdecl fstat32i64(int fd, struct _stat32i64 *stat); int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat); int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat) { struct _stat32i64 st; - int ret = __MINGW_FIXED_STAT(_wstat32i64, _Filename, &st); + int ret = __MINGW_FIXED_STAT(fstat32i64, _wstat32i64, _Filename, &st); if (ret != 0) return ret; if (st.st_size > INT32_MAX) { diff --git a/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c b/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c index f8ed493e71c8..42662878c2c0 100644 --- a/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c +++ b/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c @@ -19,7 +19,7 @@ int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat); int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat) { struct _stat64 st; - int ret = __MINGW_FIXED_STAT(_wstat64, _Filename, &st); + int ret = __MINGW_FIXED_STAT(fstat64, _wstat64, _Filename, &st); if (ret != 0) return ret; if (st.st_size > INT32_MAX) { diff --git a/mingw-w64-crt/stdio/stat32i64.c b/mingw-w64-crt/stdio/stat32i64.c index f8e84bfd0ff9..ddde9205f591 100644 --- a/mingw-w64-crt/stdio/stat32i64.c +++ b/mingw-w64-crt/stdio/stat32i64.c @@ -8,9 +8,10 @@ #include <stdlib.h> #include "__mingw_fix_stat.h" +int __cdecl fstat32i64(int fd, struct _stat32i64 *stat); int __cdecl stat32i64(const char *_Filename, struct _stat32i64 *_Stat); int __cdecl stat32i64(const char *_Filename, struct _stat32i64 *_Stat) { - return __MINGW_FIXED_STAT(_stat32i64, _Filename, _Stat); + return __MINGW_FIXED_STAT(fstat32i64, _stat32i64, _Filename, _Stat); } 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 index 0cef610b6dd0..ab8afd637a9d 100644 --- a/mingw-w64-crt/stdio/stat64.c +++ b/mingw-w64-crt/stdio/stat64.c @@ -10,6 +10,6 @@ int __cdecl stat64(const char *_Filename, struct stat64 *_Stat) { - return __MINGW_FIXED_STAT(_stat64, _Filename, (struct _stat64 *)_Stat); + return __MINGW_FIXED_STAT(fstat64, _stat64, _Filename, (struct _stat64 *)_Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(stat64))(const char *, struct stat64 *) = stat64; diff --git a/mingw-w64-crt/stdio/wstat32i64.c b/mingw-w64-crt/stdio/wstat32i64.c index d4bb87eaf2d3..240635538ece 100644 --- a/mingw-w64-crt/stdio/wstat32i64.c +++ b/mingw-w64-crt/stdio/wstat32i64.c @@ -8,9 +8,10 @@ #include <stdlib.h> #include "__mingw_fix_stat.h" +int __cdecl fstat32i64(int fd, struct _stat32i64 *stat); int __cdecl wstat32i64(const wchar_t *_Filename, struct _stat32i64 *_Stat); int __cdecl wstat32i64(const wchar_t *_Filename, struct _stat32i64 *_Stat) { - return __MINGW_FIXED_STAT(_wstat32i64, _Filename, _Stat); + return __MINGW_FIXED_STAT(fstat32i64, _wstat32i64, _Filename, _Stat); } 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 index 0f00a4ed628a..dbcf958a3b15 100644 --- a/mingw-w64-crt/stdio/wstat64.c +++ b/mingw-w64-crt/stdio/wstat64.c @@ -10,6 +10,6 @@ int __cdecl wstat64(const wchar_t *_Filename, struct stat64 *_Stat) { - return __MINGW_FIXED_STAT(_wstat64, _Filename, (struct _stat64 *)_Stat); + return __MINGW_FIXED_STAT(fstat64, _wstat64, _Filename, (struct _stat64 *)_Stat); } int (__cdecl *__MINGW_IMP_SYMBOL(wstat64))(const wchar_t *, struct stat64 *) = wstat64; diff --git a/mingw-w64-crt/testcases/t_stat.c b/mingw-w64-crt/testcases/t_stat.c index 33fa4a1f5a04..a1ddfc1163e9 100644 --- a/mingw-w64-crt/testcases/t_stat.c +++ b/mingw-w64-crt/testcases/t_stat.c @@ -1,15 +1,116 @@ #include <stdio.h> +#include <assert.h> #include <sys/types.h> #include <sys/stat.h> +#include <windows.h> -int main(int argc, char *argv[]) +#define type(mode) (S_ISDIR(mode) ? "DIR" : S_ISFIFO(mode) ? "FIFO" : S_ISCHR(mode) ? "CHR" : S_ISBLK(mode) ? "BLK" : S_ISREG(mode) ? "REG" : "UNKN") + +int main(int argc __attribute__((unused)), char *argv[]) { + char windows_dir_path[MAX_PATH + 1]; + char buffer[sizeof(windows_dir_path) + 128]; struct stat st; struct stat64 st64; + DWORD raw_ver; + unsigned ver; + unsigned type; + if (0 == stat(argv[0], &st)) printf("mode = %x\n", st.st_mode); if (0 == stat64(argv[0], &st64)) printf("mode = %x\n", st64.st_mode); + + assert(stat("nul", &st) == 0); + printf("nul: %s\n", type(st.st_mode)); + assert(S_ISCHR(st.st_mode)); + + assert(stat("NUL", &st) == 0); + printf("NUL: %s\n", type(st.st_mode)); + assert(S_ISCHR(st.st_mode)); + + if (!GetWindowsDirectoryA(windows_dir_path, MAX_PATH)) + return 0; + + raw_ver = GetVersion(); + ver = ((raw_ver & 0xff) << 8) | ((raw_ver & 0xff00) >> 8); + type = (raw_ver >> 30) ^ 2; + + if (type != VER_PLATFORM_WIN32_NT) + return 0; + + snprintf(buffer, sizeof(buffer), "\\\\.\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + /* WinAPI namespace \\?\ is supported since Windows NT 3.50 */ + if (ver < MAKEWORD(50, 3)) /* pre NT 3.50 */ + return 0; + + snprintf(buffer, sizeof(buffer), "\\\\?\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + /* NT \??\Global and NT \??\GLOBALROOT is available since Windows 2000 */ + if (ver < _WIN32_WINNT_WIN2K) + return 0; + + snprintf(buffer, sizeof(buffer), "\\\\.\\Global\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + snprintf(buffer, sizeof(buffer), "\\\\?\\Global\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + snprintf(buffer, sizeof(buffer), "\\\\.\\GLOBALROOT\\??\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + snprintf(buffer, sizeof(buffer), "\\\\?\\GLOBALROOT\\??\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + snprintf(buffer, sizeof(buffer), "\\\\.\\GLOBALROOT\\DosDevices\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + snprintf(buffer, sizeof(buffer), "\\\\?\\GLOBALROOT\\DosDevices\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + /* NT \GLOBAL?? is available since Windows XP */ + if (ver < _WIN32_WINNT_WINXP) + return 0; + + snprintf(buffer, sizeof(buffer), "\\\\.\\GLOBALROOT\\GLOBAL??\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + snprintf(buffer, sizeof(buffer), "\\\\?\\GLOBALROOT\\GLOBAL??\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + snprintf(buffer, sizeof(buffer), "\\\\.\\GLOBALROOT\\GLOBAL??\\GLOBALROOT\\??\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + + snprintf(buffer, sizeof(buffer), "\\\\?\\GLOBALROOT\\GLOBAL??\\GLOBALROOT\\??\\%s", windows_dir_path); + assert(stat(buffer, &st) == 0); + printf("%s: %s\n", buffer, type(st.st_mode)); + assert(S_ISDIR(st.st_mode)); + return 0; } -- 2.20.1
_______________________________________________ Mingw-w64-public mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/mingw-w64-public
