On 2025-02-22 Pali Rohár wrote: > On Saturday 22 February 2025 16:03:42 Lasse Collin wrote: > > On 2025-02-18 Pali Rohár wrote: > > > On Tuesday 18 February 2025 23:32:54 Lasse Collin wrote: > > > > On 2025-02-18 Pali Rohár wrote: > > > > > Just one test case, can you check that your new readdir() > > > > > function is working correctly on these two paths? > > > > > > > > > > \\?\GLOBALROOT\Device\Harddisk0\Partition1\ > > > > > \\?\GLOBALROOT\Device\HardiskVolume1\ > > > > > > > > These paths don't work with the old dirent. opendir fails with > > > > ENOENT. > > > > > > Perfect, this is then nice improvement, that in new version it is > > > working. > > > > I had made a mistake. I had tested the new code with and without \ > > at the end, but I had tested the old code only without. The old > > code does work when there is \ at the end. I hope it is OK that the > > new code works without \ too, even though I guess it's not strictly > > correct. > > Uff, I'm not sure. As without the trailing \ the above path is not > directory.
Right, it's not a directory. The old dirent code calls GetFileAttributes on the path first and only then proceeds to append \* for _findfirst. The new code doesn't call GetFileAttributes at all, so FindFirstFileW is called with \* appended when there isn't a \ or / at the end already. If GetFileAttributes was called, maybe FindFirstFileW wouldn't need to check for as many Windows error codes, but one has to check for a few codes from GetAttributes still. Also, something in the file system can in theory change before FindFirstFileW is called (a race condition). Are there other situations where a path isn't a directory if there isn't a \ at the end? > > (1) > > I tested on a directory that has an unsupported reparse tag. > > FindFirstFileW fails with ERROR_CANT_ACCESS_FILE (1920) which > > currently becomes EIO. The old dirent code fails with EINVAL at > > readdir (not at opendir). > > That is fine. Thanks! > > MSVCRT's strerror(ELOOP) returns "Unknown error". UCRT has a proper > > message for ELOOP. > > Older msvcr* versions probably do not support ELOOP. It could be fixed > by introducing a wrapper into mingw-w64 which adds the missing > translation. Since ELOOP should be uncommon, "Unknown error" should be fine. The message from UCRT is correct only for symlink loop; it's misleading if the reason is invalid data in a reparse point. That is, if ELOOP feels otherwise better than ENOENT for ERROR_CANT_RESOLVE_FILENAME, the lack of message in MSVCRT shouldn't matter. It was just one tiny thing I happened to spot. > > (3) > > I found old Microsoft docs on the web which, if they can trusted, > > say that WC_NO_BEST_FIT_CHARS isn't available on Win95 and NT4. > > That is truth. I think that I have already mentioned it. Official doc: > https://web.archive.org/web/20070224052921/http://msdn.microsoft.com/library/en-us/intl/unicode_17si.asp > https://web.archive.org/web/20070320075443/http://msdn.microsoft.com/library/en-us/intl/unicode_2bj9.asp Sorry, you might have. Thanks! > For detection if WC_NO_BEST_FIT_CHARS is not supported, you can do: > > #ifdef __i386__ > WORD version = LOWORD(GetVersion()); [...] > App-local deployment should be possible. Just would require to prepare > manifest and copy all DLLs. More work but I think nothing which > mingw-w64 could prevent or could disallow. OK. Since _wopendir already catches 95/98/ME, it's fine to assume WinNT in readdir. WinXP had a x86-64 version too, so the CP_UTF8 case would need to be checked even outside i386. DWORD version = GetVersion () & 0xFF; DWORD flags = (cp == CP_UTF8) ? (version >= 6 ? WC_ERR_INVALID_CHARS : 0) : (version >= 5 ? WC_NO_BEST_FIT_CHARS : 0); This doesn't seem to make a performance difference, so caching the values doesn't seem worth any extra code. I attached this (without much testing). Preventing lossy conversion on those OSes would require more code, and that doesn't feel worth it. An alternative would be to call GetLastError() == ERROR_INVALID_FLAGS if WideCharToMultiByte fails and retry with flags = 0. I wonder if NT4 i386 and XP x64 really are worth even these short extra bits of code. But since performance doesn't get worse, maybe it's OK as long as the code is correct for sure. (For example, compatibility settings can make GetVersion lie to some extent.) -- Lasse Collin
From 4905058fb760051e85f1f4f2c93c1837c60ce01b Mon Sep 17 00:00:00 2001 From: Lasse Collin <lasse.col...@tukaani.org> Date: Sat, 22 Feb 2025 17:48:11 +0200 Subject: [PATCH] ... Support XP x64 and NT4 --- mingw-w64-crt/misc/dirent.c | 6 ++++-- mingw-w64-headers/crt/dirent.h | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mingw-w64-crt/misc/dirent.c b/mingw-w64-crt/misc/dirent.c index c9fbcef3e..8c32758b6 100644 --- a/mingw-w64-crt/misc/dirent.c +++ b/mingw-w64-crt/misc/dirent.c @@ -23,7 +23,7 @@ * - added d_type to struct dirent and struct _wdirent * - improved error handling * - added API docs into dirent.h - * - Windows 95/98/ME and NT4 are no longer supported + * - Windows 95/98/ME are no longer supported */ #ifndef WIN32_LEAN_AND_MEAN @@ -609,8 +609,10 @@ readdir_impl (DIR *dirp, BOOL fallback8dot3) * d_name is big enough that conversion cannot run out of buffer space * with double-byte character sets or UTF-8. */ + DWORD version = GetVersion () & 0xFF; unsigned int cp = get_code_page (); - DWORD flags = (cp == CP_UTF8) ? WC_ERR_INVALID_CHARS : WC_NO_BEST_FIT_CHARS; + DWORD flags = (cp == CP_UTF8) ? (version >= 6 ? WC_ERR_INVALID_CHARS : 0) + : (version >= 5 ? WC_NO_BEST_FIT_CHARS : 0); BOOL was_lossy = FALSE; BOOL *was_lossy_ptr = (cp == CP_UTF8) ? NULL : &was_lossy; diff --git a/mingw-w64-headers/crt/dirent.h b/mingw-w64-headers/crt/dirent.h index a6f9aeee3..7a27f725c 100644 --- a/mingw-w64-headers/crt/dirent.h +++ b/mingw-w64-headers/crt/dirent.h @@ -85,8 +85,7 @@ typedef struct __dirent_DIR DIR; * Windows reports as ERROR_CANT_RESOLVE_FILENAME. * EACCES Access denied. * EIO Unknown error, possibly an I/O error. - * ENOSYS This dirent implementation works on Windows 2000 and later. - * Windows 95/98/ME and NT4 are not supported. + * ENOSYS This dirent implementation doesn't work on Windows 95/98/ME. */ DIR* __cdecl __MINGW_NOTHROW opendir (const char*); -- 2.48.1
_______________________________________________ Mingw-w64-public mailing list Mingw-w64-public@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/mingw-w64-public