GetFinalPathNameByHandle canonicalizes the path of an open handle, which costs a round trip into the file system for every file that is stat'ed. Only the file name suffix is needed here, and GetFileInformationByHandleEx with argument FileNameInfo answers from the handle itself.
* lib/stat-w32.c (GetFinalPathNameByHandleFuncType) (GetFinalPathNameByHandleFunc): Remove, along with the GetFinalPathNameByHandle and VOLUME_NAME_NONE macros. (GetFileInformationByHandleExFuncType, GetFileInformationByHandleExFunc): Declare regardless of _GL_WINDOWS_STAT_INODES, as the file name lookup needs them too. (initialize): Do not resolve GetFinalPathNameByHandleA. (_gl_fstat_by_handle): Determine the file name through GetFileInformationByHandleEx with argument FileNameInfo, and convert it with WideCharToMultiByte. Signed-off-by: Oleg Tolmatcev <[email protected]> --- ChangeLog | 12 +++++++++++ lib/stat-w32.c | 55 +++++++++++++++++++++----------------------------- 2 files changed, 35 insertions(+), 32 deletions(-) I benchmarked a native grep on a GCC checkout and got up to a 17% improvement. My copyright assignment is already on file with the FSF. diff --git a/ChangeLog b/ChangeLog index 7fb59f0be5..eeedca45af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2026-09-05 Oleg Tolmatcev <[email protected]> + + stat-w32: determine the file name through GetFileInformationByHandleEx + * lib/stat-w32.c (GetFinalPathNameByHandleFuncType) + (GetFinalPathNameByHandleFunc): Remove. + (GetFileInformationByHandleExFuncType) + (GetFileInformationByHandleExFunc): Declare regardless of + _GL_WINDOWS_STAT_INODES. + (initialize): Do not resolve GetFinalPathNameByHandleA. + (_gl_fstat_by_handle): Determine the file name through + GetFileInformationByHandleEx with argument FileNameInfo. + 2026-09-04 Oleg Tolmatcev <[email protected]> fts: don't use FTS_CWDFD where openat is emulated via fchdir diff --git a/lib/stat-w32.c b/lib/stat-w32.c index 583ef09b0c..883c7c5951 100644 --- a/lib/stat-w32.c +++ b/lib/stat-w32.c @@ -54,13 +54,6 @@ /* Don't assume that UNICODE is not defined. */ #undef LoadLibrary #define LoadLibrary LoadLibraryA -#undef GetFinalPathNameByHandle -#define GetFinalPathNameByHandle GetFinalPathNameByHandleA - -/* Older mingw headers do not define VOLUME_NAME_NONE. */ -#ifndef VOLUME_NAME_NONE -# define VOLUME_NAME_NONE 4 -#endif #if !WIN32_ASSUME_VISTA @@ -68,20 +61,12 @@ # define GetProcAddress \ (void *) GetProcAddress -# if _GL_WINDOWS_STAT_INODES == 2 /* GetFileInformationByHandleEx was introduced only in Windows Vista. */ typedef DWORD (WINAPI * GetFileInformationByHandleExFuncType) (HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS fiClass, LPVOID lpBuffer, DWORD dwBufferSize); static GetFileInformationByHandleExFuncType GetFileInformationByHandleExFunc = NULL; -# endif -/* GetFinalPathNameByHandle was introduced only in Windows Vista. */ -typedef DWORD (WINAPI * GetFinalPathNameByHandleFuncType) (HANDLE hFile, - LPSTR lpFilePath, - DWORD lenFilePath, - DWORD dwFlags); -static GetFinalPathNameByHandleFuncType GetFinalPathNameByHandleFunc = NULL; static BOOL initialized = FALSE; static void @@ -89,21 +74,14 @@ initialize (void) { HMODULE kernel32 = LoadLibrary ("kernel32.dll"); if (kernel32 != NULL) - { -# if _GL_WINDOWS_STAT_INODES == 2 - GetFileInformationByHandleExFunc = - (GetFileInformationByHandleExFuncType) GetProcAddress (kernel32, "GetFileInformationByHandleEx"); -# endif - GetFinalPathNameByHandleFunc = - (GetFinalPathNameByHandleFuncType) GetProcAddress (kernel32, "GetFinalPathNameByHandleA"); - } + GetFileInformationByHandleExFunc = + (GetFileInformationByHandleExFuncType) GetProcAddress (kernel32, "GetFileInformationByHandleEx"); initialized = TRUE; } #else # define GetFileInformationByHandleExFunc GetFileInformationByHandleEx -# define GetFinalPathNameByHandleFunc GetFinalPathNameByHandle #endif @@ -272,21 +250,34 @@ _gl_fstat_by_handle (HANDLE h, const char *path, struct stat *buf) name suffix. If the file name is already known, use it. Otherwise, for non-empty files, it can be determined through - GetFinalPathNameByHandle - <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea> - or through GetFileInformationByHandleEx with argument FileNameInfo <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getfileinformationbyhandleex> <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_file_name_info> - Both require -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */ + This requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */ if (info.nFileSizeHigh > 0 || info.nFileSizeLow > 0) { char fpath[PATH_MAX]; + /* Room for the header and for the file name. */ + union + { + FILE_NAME_INFO info; + char storage[sizeof (FILE_NAME_INFO) + + PATH_MAX * sizeof (WCHAR)]; + } fni; + int len; if (path != NULL - || (GetFinalPathNameByHandleFunc != NULL - && GetFinalPathNameByHandleFunc (h, fpath, sizeof (fpath), VOLUME_NAME_NONE) - < sizeof (fpath) - && (path = fpath, 1))) + || (GetFileInformationByHandleExFunc != NULL + && GetFileInformationByHandleExFunc (h, FileNameInfo, + &fni.info, + sizeof (fni)) + && ((len = WideCharToMultiByte (CP_ACP, 0, + fni.info.FileName, + fni.info.FileNameLength + / sizeof (WCHAR), + fpath, sizeof (fpath) - 1, + NULL, NULL)) + > 0) + && (fpath[len] = '\0', path = fpath, 1))) { const char *last_dot = NULL; for (const char *p = path; *p != '\0'; p++) -- 2.55.0.windows.5
