]> git.eshelyaron.com Git - emacs.git/commitdiff
Make file-accessible-directory-p reliable on MS-Windows
authorEli Zaretskii <eliz@gnu.org>
Mon, 31 Aug 2015 14:48:26 +0000 (17:48 +0300)
committerEli Zaretskii <eliz@gnu.org>
Mon, 31 Aug 2015 14:48:26 +0000 (17:48 +0300)
* src/w32.c (w32_accessible_directory_p): New function.
* src/w32.h (w32_accessible_directory_p): Add prototype.
* src/fileio.c (file_accessible_directory_p) [WINDOWSNT]: Call
w32_accessible_directory_p to test a directory for accessibility
by the current user.  (Bug#21346)
(Ffile_accessible_directory_p): Remove the w32 specific caveat
from the doc string.

src/fileio.c
src/w32.c
src/w32.h

index debd1f30a4fe4d111356cc38ca741ce7238f27e6..a36dfbcfa364b78a0546b04ec823bbc8bad3669d 100644 (file)
@@ -2655,11 +2655,7 @@ and the directory must allow you to open files in it.  In order to use a
 directory as a buffer's current directory, this predicate must return true.
 A directory name spec may be given instead; then the value is t
 if the directory so specified exists and really is a readable and
-searchable directory.
-
-The result might be a false positive on MS-Windows in some rare cases,
-i.e., this function could return t for a directory that is not
-accessible by the current user.  */)
+searchable directory.  */)
   (Lisp_Object filename)
 {
   Lisp_Object absname;
@@ -2689,10 +2685,18 @@ bool
 file_accessible_directory_p (Lisp_Object file)
 {
 #ifdef DOS_NT
-  /* There's no need to test whether FILE is searchable, as the
-     searchable/executable bit is invented on DOS_NT platforms.  */
+# ifdef WINDOWSNT
+  /* We need a special-purpose test because (a) NTFS security data is
+     not reflected in Posix-style mode bits, and (b) the trick with
+     accessing "DIR/.", used below on Posix hosts, doesn't work on
+     Windows, because "DIR/." is normalized to just "DIR" before
+     hitting the disk.  */
+  return (SBYTES (file) == 0
+         || w32_accessible_directory_p (SSDATA (file), SBYTES (file)));
+# else /* MSDOS */
   return file_directory_p (SSDATA (file));
-#else
+# endif         /* MSDOS */
+#else   /* !DOS_NT */
   /* On POSIXish platforms, use just one system call; this avoids a
      race and is typically faster.  */
   const char *data = SSDATA (file);
@@ -2725,7 +2729,7 @@ file_accessible_directory_p (Lisp_Object file)
   SAFE_FREE ();
   errno = saved_errno;
   return ok;
-#endif
+#endif /* !DOS_NT */
 }
 
 DEFUN ("file-regular-p", Ffile_regular_p, Sfile_regular_p, 1, 1, 0,
index b421667e241679ae5a0366cf66e15b258e501184..60fbe92e0827f4f095566be51262dd9f8e5473cb 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -3847,6 +3847,57 @@ faccessat (int dirfd, const char * path, int mode, int flags)
   return 0;
 }
 
+/* A special test for DIRNAME being a directory accessible by the
+   current user.  This is needed because the security permissions in
+   directory's ACLs are not visible in the Posix-style mode bits
+   returned by 'stat' and in attributes returned by GetFileAttributes.
+   So a directory would seem like it's readable by the current user,
+   but will in fact error out with EACCES when they actually try.  */
+int
+w32_accessible_directory_p (const char *dirname, ptrdiff_t dirlen)
+{
+  char pattern[MAX_UTF8_PATH];
+  bool last_slash = dirlen > 0 && IS_DIRECTORY_SEP (dirname[dirlen - 1]);
+  HANDLE dh;
+
+  strcpy (pattern, map_w32_filename (dirname, NULL));
+
+  /* Note: No need to resolve symlinks in FILENAME, because FindFirst
+     opens the directory that is the target of a symlink.  */
+  if (w32_unicode_filenames)
+    {
+      wchar_t pat_w[MAX_PATH + 2];
+      WIN32_FIND_DATAW dfd_w;
+
+      filename_to_utf16 (pattern, pat_w);
+      if (!last_slash)
+       wcscat (pat_w, L"\\");
+      wcscat (pat_w, L"*");
+      dh = FindFirstFileW (pat_w, &dfd_w);
+    }
+  else
+    {
+      char pat_a[MAX_PATH + 2];
+      WIN32_FIND_DATAA dfd_a;
+
+      filename_to_ansi (pattern, pat_a);
+      if (!last_slash)
+       strcpy (pat_a, "\\");
+      strcat (pat_a, "*");
+      /* In case DIRNAME cannot be expressed in characters from the
+        current ANSI codepage.  */
+      if (_mbspbrk (pat_a, "?"))
+       dh = INVALID_HANDLE_VALUE;
+      else
+       dh = FindFirstFileA (pat_a, &dfd_a);
+    }
+
+  if (dh == INVALID_HANDLE_VALUE)
+    return 0;
+  FindClose (dh);
+  return 1;
+}
+
 /* A version of 'access' to be used locally with file names in
    locale-specific encoding.  Does not resolve symlinks and does not
    support file names on FAT12 and FAT16 volumes, but that's OK, since
index 338cb06b193cf84cd8e6b6bb3d2bbc1d11d54761..2c711502593aff9f4247b845f7d0b80ca573b8db 100644 (file)
--- a/src/w32.h
+++ b/src/w32.h
@@ -195,6 +195,7 @@ extern int  filename_to_utf16 (const char *, wchar_t *);
 extern int  codepage_for_filenames (CPINFO *);
 extern Lisp_Object ansi_encode_filename (Lisp_Object);
 extern int  w32_copy_file (const char *, const char *, int, int, int);
+extern int  w32_accessible_directory_p (const char *, ptrdiff_t);
 
 extern BOOL init_winsock (int load_now);
 extern void srandom (int);