]> git.eshelyaron.com Git - emacs.git/commitdiff
Avoid unnecessary calls to GetFileAttributes on MS-Windows
authorEli Zaretskii <eliz@gnu.org>
Sun, 11 May 2025 16:27:13 +0000 (19:27 +0300)
committerEshel Yaron <me@eshelyaron.com>
Mon, 12 May 2025 19:55:39 +0000 (21:55 +0200)
* src/w32.c (access_attrs): New function, refactored from
'faccessat'.
(faccessat): Call 'access_attrs' early to determine whether the
file doesn't exist, and if so, avoid calling 'chase_symlinks'.
Also avoid calling 'chase_symlinks' if it is known that the file
cannot be a symlink, given its attributes.  (Bug#78341)

(cherry picked from commit 882c849034a909a62179c38ee01cc08572fa1a68)

src/w32.c

index c504c141b311e6ac9f2a45654659fcfc3acc49dd..5de721ad71f910011d96d91e0b7711792a9da220 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -4108,6 +4108,69 @@ logon_network_drive (const char *path)
     }
 }
 
+/* Subroutine of faccessat.  Determines attributes of FILE (which is
+   assumed to be in UTF-8 and after map_w32_filename) as reported by
+   GetFileAttributes.  Returns -1 if it fails (meaning the file doesn't
+   exist or cannot be accessed by the current user), otherwise returns
+   the bitmap of file's attributes.  */
+static DWORD
+access_attrs (const char *file)
+{
+  DWORD attrs;
+
+  if (w32_unicode_filenames)
+    {
+      wchar_t file_w[MAX_PATH];
+
+      filename_to_utf16 (file, file_w);
+      attrs = GetFileAttributesW (file_w);
+    }
+  else
+    {
+      char file_a[MAX_PATH];
+
+      filename_to_ansi (file, file_a);
+      attrs = GetFileAttributesA (file_a);
+    }
+
+  if (attrs == -1)
+    {
+      DWORD w32err = GetLastError ();
+
+      switch (w32err)
+       {
+       case ERROR_INVALID_NAME:
+       case ERROR_BAD_PATHNAME:
+         if (is_unc_volume (file))
+           {
+             attrs = unc_volume_file_attributes (file);
+             if (attrs == -1)
+               {
+                 errno = EACCES;
+                 return -1;
+               }
+             return attrs;
+           }
+         /* FALLTHROUGH */
+         FALLTHROUGH;
+       case ERROR_FILE_NOT_FOUND:
+       case ERROR_PATH_NOT_FOUND:
+       case ERROR_INVALID_DRIVE:
+       case ERROR_NOT_READY:
+       case ERROR_BAD_NETPATH:
+       case ERROR_BAD_NET_NAME:
+         errno = ENOENT;
+         break;
+       default:
+         errno = EACCES;
+         break;
+       }
+      return -1;
+    }
+
+  return attrs;
+}
+
 /* Emulate faccessat(2).  */
 int
 faccessat (int dirfd, const char * path, int mode, int flags)
@@ -4142,65 +4205,26 @@ faccessat (int dirfd, const char * path, int mode, int flags)
   /* MSVCRT implementation of 'access' doesn't recognize D_OK, and its
      newer versions blow up when passed D_OK.  */
   path = map_w32_filename (path, NULL);
+
+  attributes = access_attrs (path);
+  if (attributes == -1)        /* PATH doesn't exist or is inaccessible */
+    return -1;
+
   /* If the last element of PATH is a symlink, we need to resolve it
      to get the attributes of its target file.  Note: any symlinks in
      PATH elements other than the last one are transparently resolved
      by GetFileAttributes below.  */
+  int not_a_symlink = ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0);
   if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0
-      && (flags & AT_SYMLINK_NOFOLLOW) == 0)
-    path = chase_symlinks (path);
-
-  if (w32_unicode_filenames)
-    {
-      wchar_t path_w[MAX_PATH];
-
-      filename_to_utf16 (path, path_w);
-      attributes = GetFileAttributesW (path_w);
-    }
-  else
-    {
-      char path_a[MAX_PATH];
-
-      filename_to_ansi (path, path_a);
-      attributes = GetFileAttributesA (path_a);
-    }
-
-  if (attributes == -1)
+      && (flags & AT_SYMLINK_NOFOLLOW) == 0
+      && !not_a_symlink)
     {
-      DWORD w32err = GetLastError ();
-
-      switch (w32err)
-       {
-       case ERROR_INVALID_NAME:
-       case ERROR_BAD_PATHNAME:
-         if (is_unc_volume (path))
-           {
-             attributes = unc_volume_file_attributes (path);
-             if (attributes == -1)
-               {
-                 errno = EACCES;
-                 return -1;
-               }
-             goto check_attrs;
-           }
-         /* FALLTHROUGH */
-         FALLTHROUGH;
-       case ERROR_FILE_NOT_FOUND:
-       case ERROR_PATH_NOT_FOUND:
-       case ERROR_INVALID_DRIVE:
-       case ERROR_NOT_READY:
-       case ERROR_BAD_NETPATH:
-       case ERROR_BAD_NET_NAME:
-         errno = ENOENT;
-         break;
-       default:
-         errno = EACCES;
-         break;
-       }
-      return -1;
+      path = chase_symlinks (path);
+      attributes = access_attrs (path);
+      if (attributes == -1)
+       return -1;
     }
 
- check_attrs:
   if ((mode & X_OK) != 0
       && !(is_exec (path) || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
     {