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


Reply via email to