summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Zaretskii <eliz@gnu.org>2015-08-31 17:48:26 +0300
committerEli Zaretskii <eliz@gnu.org>2015-08-31 17:48:26 +0300
commit697be62c5f2b86e8ad93dfcaa0df07890c24d989 (patch)
tree5e6b5d4ea9cd75bbc2eca0d7831b1364b6bc333a
parent8af8355c3f72500986f6f10b62714b228d6f35ee (diff)
Make file-accessible-directory-p reliable on MS-Windows
* 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.
-rw-r--r--src/fileio.c22
-rw-r--r--src/w32.c51
-rw-r--r--src/w32.h1
3 files changed, 65 insertions, 9 deletions
diff --git a/src/fileio.c b/src/fileio.c
index debd1f30a4..a36dfbcfa3 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -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,
diff --git a/src/w32.c b/src/w32.c
index b421667e24..60fbe92e08 100644
--- 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
diff --git a/src/w32.h b/src/w32.h
index 338cb06b19..2c71150259 100644
--- 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);