On 2025-02-23 Lasse Collin wrote: > On 2025-02-23 Pali Rohár wrote: > > WinAPI \\?\ has meaning to not do any normalization, so consumer > > should not add or remove some characters. [...] > In the new dirent, GetFullPathNameW converts mixed \/ usage to \, then > * is appended and FindFirstFileW is called. The old code calls > GetFileAttributes before constructing a full path
Even the old dirent has a problem: \??\ is broken because "\??\C:\" works with GetFileAttributes, but _fullpath converts it to "C:\??\C:\", and then readdir fails. I attached a patch to the new dirent. It's shorter to read if white space change is ignored. It skips GetFullPathNameW with the \\?\ and \??\ prefixes. I didn't test but maybe this helps if there is a weird directory name which doesn't survive filename normalization. I will comment GetFileAttributesW considerations later. -- Lasse Collin
From c849f103459c12599bcaf0d7f6674bb8541f02fe Mon Sep 17 00:00:00 2001 From: Lasse Collin <lasse.col...@tukaani.org> Date: Mon, 24 Feb 2025 16:30:00 +0200 Subject: [PATCH] ... Skip GetFullPathNameW for \\?\ and \??\ paths --- mingw-w64-crt/misc/dirent.c | 93 ++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 28 deletions(-) diff --git a/mingw-w64-crt/misc/dirent.c b/mingw-w64-crt/misc/dirent.c index 1f7a726ce..ffea4adc7 100644 --- a/mingw-w64-crt/misc/dirent.c +++ b/mingw-w64-crt/misc/dirent.c @@ -40,7 +40,8 @@ /* Maximum number of wide characters allowed in a pathname including - * the terminating \0. This constant is based on the behavior of + * the terminating \0. This is the limit for paths that begin with \\?\ + * and for all paths in long path aware applications. * GetFullPathNameW which rejects input strings that exceed this size. */ #define DIRENT_WPATH_MAX 32767 @@ -122,6 +123,7 @@ get_code_page (void) DIR * _wopendir (const wchar_t *path) { + BOOL normalize_path; DWORD full_path_alloc_size; DWORD full_path_len; int err; @@ -141,31 +143,52 @@ _wopendir (const wchar_t *path) } /* - * Make an absolute pathname. + * We need an absolute pathname so that things keep working even if + * the current directory is changed. * - * When buffer is too small or not provided, the return value of - * GetFullPathNameW includes the space needed for the terminating \0. + * If the pathname begins with "\\?\" or "\??\" it is used as is. + * Otherwise GetFullPathNameW is used, including variations with + * forward slashes like "//?/" or "/\?\". */ - full_path_alloc_size = GetFullPathNameW (path, 0, NULL, NULL); - if (full_path_alloc_size == 0) + if (path[0] == '\\' && + (path[1] == '\\' || path[1] == '?') && + path[2] == '?' && + path[3] == '\\') { - switch (GetLastError ()) + normalize_path = FALSE; + full_path_alloc_size = wcslen (path) + 1; + if (full_path_alloc_size > DIRENT_WPATH_MAX) { - case ERROR_CALL_NOT_IMPLEMENTED: - /* Windows 95/98/ME is not supported by this dirent code. */ - errno = ENOSYS; - return NULL; + errno = ENAMETOOLONG; + return NULL; + } + } + else + { + /* When buffer is too small or not provided, the return value of + * GetFullPathNameW includes the space needed for the terminating \0. */ + normalize_path = TRUE; + full_path_alloc_size = GetFullPathNameW (path, 0, NULL, NULL); + if (full_path_alloc_size == 0) + { + switch (GetLastError ()) + { + case ERROR_CALL_NOT_IMPLEMENTED: + /* Windows 95/98/ME is not supported by this dirent code. */ + errno = ENOSYS; + return NULL; - case ERROR_FILENAME_EXCED_RANGE: - /* The limit is 32767 wide chars including the terminating \0. */ - errno = ENAMETOOLONG; - return NULL; + case ERROR_FILENAME_EXCED_RANGE: + /* See the comment of DIRENT_WPATH_MAX in this file. */ + errno = ENAMETOOLONG; + return NULL; - default: - /* GetFullPathNameW accepts various invalid inputs so errors - * should be uncommon. */ - errno = ENOENT; - return NULL; + default: + /* GetFullPathNameW accepts various invalid inputs so errors + * should be uncommon. */ + errno = ENOENT; + return NULL; + } } } @@ -179,15 +202,24 @@ _wopendir (const wchar_t *path) return NULL; } - /* On success, the return value from GetFullPathNameW does not include - * the terminating \0. */ - full_path_len = GetFullPathNameW (path, full_path_alloc_size, - dirp->dd_name, NULL); - if (full_path_len >= full_path_alloc_size) + if (normalize_path) { - free (dirp); - errno = ENOENT; - return NULL; + /* On success, the return value from GetFullPathNameW does not include + * the terminating \0. */ + full_path_len = GetFullPathNameW (path, full_path_alloc_size, + dirp->dd_name, NULL); + if (full_path_len >= full_path_alloc_size) + { + free (dirp); + errno = ENOENT; + return NULL; + } + } + else + { + /* Copy the \\?\ or \??\ pathname as is. */ + memcpy (dirp->dd_name, path, full_path_alloc_size * sizeof (wchar_t)); + full_path_len = full_path_alloc_size - 1; } /* Create the search expression: @@ -308,6 +340,11 @@ _wopendir (const wchar_t *path) * That error shouldn't be possible with FindFirstFileW. */ + case ERROR_CALL_NOT_IMPLEMENTED: + /* We might get here if GetFullPathNameW wasn't called. */ + errno = ENOSYS; + return NULL; + default: /* Unknown error. */ err = EIO; -- 2.48.1
_______________________________________________ Mingw-w64-public mailing list Mingw-w64-public@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/mingw-w64-public