aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2012-11-13 20:55:41 -0800
committerPaul Eggert2012-11-13 20:55:41 -0800
commit73dcdb9f30cb94a3183db54d9b463370c3978d4d (patch)
tree216d47d5bc96bce2a4ca87a57967b0e1c3c151f2 /src
parent9c3912d3d9aaa1e20e3f7168f5764695ad5e43fd (diff)
downloademacs-73dcdb9f30cb94a3183db54d9b463370c3978d4d.tar.gz
emacs-73dcdb9f30cb94a3183db54d9b463370c3978d4d.zip
Use faccessat, not access, when checking file permissions.
This fixes a bug that has been present in Emacs since its creation. It was reported by Chris Torek in 1983 even before GNU Emacs existed, which must set some sort of record. (Torek's bug report was against a predecessor of GNU Emacs, but GNU Emacs happened to have the same common flaw.) See Torek's Usenet posting "setuid/setgid programs & Emacs" Article-I.D.: sri-arpa.858 Posted: Fri Apr 8 14:18:56 1983. * .bzrignore: Add lib/fcntl.h. * configure.ac (euidaccess): Remove check; gnulib does this for us now. (gl_FCNTL_O_FLAGS): Define a dummy version. * lib/at-func.c, lib/euidaccess.c, lib/faccessat.c, lib/fcntl.in.h: * lib/getgroups.c, lib/group-member.c, lib/root-uid.h: * lib/xalloc-oversized.h, m4/euidaccess.m4, m4/faccessat.m4: * m4/fcntl_h.m4, m4/getgroups.m4, m4/group-member.m4: New files, from gnulib. * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. * admin/merge-gnulib (GNULIB_MODULES): Add faccessat. (GNULIB_TOOL_FLAGS): Avoid at-internal, fchdir, malloc-posix, openat-die, openat-h, save-cwd. Do not avoid fcntl-h. Omit gnulib's m4/fcntl-o.m4. * nt/inc/ms-w32.h (AT_FDCWD, AT_EACCESS): New symbols. (access): Remove. (faccessat): New macro. * src/Makefile.in (LIB_EACCESS): New macro. (LIBES): Use it. * src/callproc.c (init_callproc): * src/charset.c (init_charset): * src/fileio.c (check_existing, check_executable, check_writable) (Ffile_readable_p): * src/lread.c (openp, load_path_check): * src/process.c (allocate_pty): * src/xrdb.c (file_p): Use effective UID when checking permissions, not real UID. * src/callproc.c (init_callproc): * src/charset.c (init_charset): * src/lread.c (load_path_check, init_lread): Test whether directories are accessible, not merely whether they exist. * src/conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): New macro. * src/fileio.c (check_existing, check_executable, check_writable) (Ffile_readable_p): Use symbolic names instead of integers for the flags, as they're portable now. (check_writable): New arg AMODE. All uses changed. Set errno on failure. (Ffile_readable_p): Use faccessat, not stat + open + close. (Ffile_writable_p): No need to call check_existing + check_writable. Just call check_writable and then look at errno. This saves a syscall. dir should never be nil; replace an unnecessary runtime check with an eassert. When checking the parent directory of a nonexistent file, check that the directory is searchable as well as writable, as we can't create files in unsearchable directories. (file_directory_p): New function, which uses 'stat' on most platforms but faccessat with D_OK (for efficiency) if WINDOWSNT. (Ffile_directory_p, Fset_file_times): Use it. (file_accessible_directory_p): New function, which uses a single syscall for efficiency. (Ffile_accessible_directory_p): Use it. * src/xrdb.c (file_p): Use file_directory_p. * src/lisp.h (file_directory_p, file_accessible_directory_p): New decls. * src/lread.c (openp): When opening a file, use fstat rather than stat, as that avoids a permissions race. When not opening a file, use file_directory_p rather than stat. (dir_warning): First arg is now a usage string, not a format. Use errno. All uses changed. * src/nsterm.m (ns_term_init): Remove unnecessary call to file-readable that merely introduced a race. * src/process.c, src/sysdep.c, src/term.c: All uses of '#ifdef O_NONBLOCK' changed to '#if O_NONBLOCK', to accommodate gnulib O_* style, and similarly for the other O_* flags. * src/w32.c (sys_faccessat): Rename from sys_access and switch to faccessat's API. All uses changed. * src/xrdb.c: Do not include <sys/stat.h>; no longer needed. (magic_db): Rename from magic_file_p. (magic_db, search_magic_path): Return an XrmDatabase rather than a char *, so that we don't have to test for file existence separately from opening the file for reading. This removes a race fixes a permission-checking problem, and simplifies the code. All uses changed. (file_p): Remove; no longer needed. Fixes: debbugs:12632
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog67
-rw-r--r--src/Makefile.in3
-rw-r--r--src/callproc.c12
-rw-r--r--src/charset.c2
-rw-r--r--src/conf_post.h4
-rw-r--r--src/fileio.c184
-rw-r--r--src/lisp.h2
-rw-r--r--src/lread.c92
-rw-r--r--src/nsterm.m2
-rw-r--r--src/process.c36
-rw-r--r--src/sysdep.c6
-rw-r--r--src/term.c4
-rw-r--r--src/w32.c18
-rw-r--r--src/xrdb.c101
14 files changed, 297 insertions, 236 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index 1d94cef6577..a6b42e8a58c 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,70 @@
12012-11-14 Paul Eggert <eggert@cs.ucla.edu>
2
3 Use faccessat, not access, when checking file permissions (Bug#12632).
4 This fixes a bug that has been present in Emacs since its creation.
5 It was reported by Chris Torek in 1983 even before GNU Emacs existed,
6 which must set some sort of record. (Torek's bug report was against
7 a predecessor of GNU Emacs, but GNU Emacs happened to have the
8 same common flaw.) See Torek's Usenet posting
9 "setuid/setgid programs & Emacs" Article-I.D.: sri-arpa.858
10 Posted: Fri Apr 8 14:18:56 1983.
11 * Makefile.in (LIB_EACCESS): New macro.
12 (LIBES): Use it.
13 * callproc.c (init_callproc):
14 * charset.c (init_charset):
15 * fileio.c (check_existing, check_executable, check_writable)
16 (Ffile_readable_p):
17 * lread.c (openp, load_path_check):
18 * process.c (allocate_pty):
19 * xrdb.c (file_p):
20 Use effective UID when checking permissions, not real UID.
21 * callproc.c (init_callproc):
22 * charset.c (init_charset):
23 * lread.c (load_path_check, init_lread):
24 Test whether directories are accessible, not merely whether they exist.
25 * conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): New macro.
26 * fileio.c (check_existing, check_executable, check_writable)
27 (Ffile_readable_p):
28 Use symbolic names instead of integers for the flags, as they're
29 portable now.
30 (check_writable): New arg AMODE. All uses changed.
31 Set errno on failure.
32 (Ffile_readable_p): Use faccessat, not stat + open + close.
33 (Ffile_writable_p): No need to call check_existing + check_writable.
34 Just call check_writable and then look at errno. This saves a syscall.
35 dir should never be nil; replace an unnecessary runtime check
36 with an eassert. When checking the parent directory of a nonexistent
37 file, check that the directory is searchable as well as writable, as
38 we can't create files in unsearchable directories.
39 (file_directory_p): New function, which uses 'stat' on most platforms
40 but faccessat with D_OK (for efficiency) if WINDOWSNT.
41 (Ffile_directory_p, Fset_file_times): Use it.
42 (file_accessible_directory_p): New function, which uses a single
43 syscall for efficiency.
44 (Ffile_accessible_directory_p): Use it.
45 * xrdb.c (file_p): Use file_directory_p.
46 * lisp.h (file_directory_p, file_accessible_directory_p): New decls.
47 * lread.c (openp): When opening a file, use fstat rather than
48 stat, as that avoids a permissions race. When not opening a file,
49 use file_directory_p rather than stat.
50 (dir_warning): First arg is now a usage string, not a format.
51 Use errno. All uses changed.
52 * nsterm.m (ns_term_init): Remove unnecessary call to file-readable
53 that merely introduced a race.
54 * process.c, sysdep.c, term.c: All uses of '#ifdef O_NONBLOCK'
55 changed to '#if O_NONBLOCK', to accommodate gnulib O_* style,
56 and similarly for the other O_* flags.
57 * w32.c (sys_faccessat): Rename from sys_access and switch to
58 faccessat's API. All uses changed.
59 * xrdb.c: Do not include <sys/stat.h>; no longer needed.
60 (magic_db): Rename from magic_file_p.
61 (magic_db, search_magic_path): Return an XrmDatabase rather than a
62 char *, so that we don't have to test for file existence
63 separately from opening the file for reading. This removes a race
64 fixes a permission-checking problem, and simplifies the code.
65 All uses changed.
66 (file_p): Remove; no longer needed.
67
12012-11-13 Dmitry Antipov <dmantipov@yandex.ru> 682012-11-13 Dmitry Antipov <dmantipov@yandex.ru>
2 69
3 Omit glyphs initialization at startup. 70 Omit glyphs initialization at startup.
diff --git a/src/Makefile.in b/src/Makefile.in
index c24e421bbbc..d034ad04796 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -150,6 +150,7 @@ M17N_FLT_CFLAGS = @M17N_FLT_CFLAGS@
150M17N_FLT_LIBS = @M17N_FLT_LIBS@ 150M17N_FLT_LIBS = @M17N_FLT_LIBS@
151 151
152LIB_CLOCK_GETTIME=@LIB_CLOCK_GETTIME@ 152LIB_CLOCK_GETTIME=@LIB_CLOCK_GETTIME@
153LIB_EACCESS=@LIB_EACCESS@
153LIB_TIMER_TIME=@LIB_TIMER_TIME@ 154LIB_TIMER_TIME=@LIB_TIMER_TIME@
154 155
155DBUS_CFLAGS = @DBUS_CFLAGS@ 156DBUS_CFLAGS = @DBUS_CFLAGS@
@@ -392,7 +393,7 @@ otherobj= $(TERMCAP_OBJ) $(PRE_ALLOC_OBJ) $(GMALLOC_OBJ) $(RALLOC_OBJ) \
392LIBES = $(LIBS) $(W32_LIBS) $(LIBX_BASE) $(LIBIMAGE) \ 393LIBES = $(LIBS) $(W32_LIBS) $(LIBX_BASE) $(LIBIMAGE) \
393 $(LIBX_OTHER) $(LIBSOUND) \ 394 $(LIBX_OTHER) $(LIBSOUND) \
394 $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \ 395 $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
395 $(LIB_TIMER_TIME) $(DBUS_LIBS) \ 396 $(LIB_EACCESS) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
396 $(LIB_EXECINFO) \ 397 $(LIB_EXECINFO) \
397 $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \ 398 $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
398 $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ 399 $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
diff --git a/src/callproc.c b/src/callproc.c
index c7bbe36e605..8ecaba2b408 100644
--- a/src/callproc.c
+++ b/src/callproc.c
@@ -1576,15 +1576,13 @@ init_callproc (void)
1576#endif 1576#endif
1577 { 1577 {
1578 tempdir = Fdirectory_file_name (Vexec_directory); 1578 tempdir = Fdirectory_file_name (Vexec_directory);
1579 if (access (SSDATA (tempdir), 0) < 0) 1579 if (! file_accessible_directory_p (SSDATA (tempdir)))
1580 dir_warning ("Warning: arch-dependent data dir (%s) does not exist.\n", 1580 dir_warning ("arch-dependent data dir", Vexec_directory);
1581 Vexec_directory);
1582 } 1581 }
1583 1582
1584 tempdir = Fdirectory_file_name (Vdata_directory); 1583 tempdir = Fdirectory_file_name (Vdata_directory);
1585 if (access (SSDATA (tempdir), 0) < 0) 1584 if (! file_accessible_directory_p (SSDATA (tempdir)))
1586 dir_warning ("Warning: arch-independent data dir (%s) does not exist.\n", 1585 dir_warning ("arch-independent data dir", Vdata_directory);
1587 Vdata_directory);
1588 1586
1589 sh = (char *) getenv ("SHELL"); 1587 sh = (char *) getenv ("SHELL");
1590 Vshell_file_name = build_string (sh ? sh : "/bin/sh"); 1588 Vshell_file_name = build_string (sh ? sh : "/bin/sh");
@@ -1593,7 +1591,7 @@ init_callproc (void)
1593 Vshared_game_score_directory = Qnil; 1591 Vshared_game_score_directory = Qnil;
1594#else 1592#else
1595 Vshared_game_score_directory = build_string (PATH_GAME); 1593 Vshared_game_score_directory = build_string (PATH_GAME);
1596 if (NILP (Ffile_directory_p (Vshared_game_score_directory))) 1594 if (NILP (Ffile_accessible_directory_p (Vshared_game_score_directory)))
1597 Vshared_game_score_directory = Qnil; 1595 Vshared_game_score_directory = Qnil;
1598#endif 1596#endif
1599} 1597}
diff --git a/src/charset.c b/src/charset.c
index 6b999824dab..c9133c780e8 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -2293,7 +2293,7 @@ init_charset (void)
2293{ 2293{
2294 Lisp_Object tempdir; 2294 Lisp_Object tempdir;
2295 tempdir = Fexpand_file_name (build_string ("charsets"), Vdata_directory); 2295 tempdir = Fexpand_file_name (build_string ("charsets"), Vdata_directory);
2296 if (access (SSDATA (tempdir), 0) < 0) 2296 if (! file_accessible_directory_p (SSDATA (tempdir)))
2297 { 2297 {
2298 /* This used to be non-fatal (dir_warning), but it should not 2298 /* This used to be non-fatal (dir_warning), but it should not
2299 happen, and if it does sooner or later it will cause some 2299 happen, and if it does sooner or later it will cause some
diff --git a/src/conf_post.h b/src/conf_post.h
index 66390ddf103..b1997e79081 100644
--- a/src/conf_post.h
+++ b/src/conf_post.h
@@ -178,6 +178,10 @@ extern void _DebPrint (const char *fmt, ...);
178#endif 178#endif
179#endif 179#endif
180 180
181/* Tell gnulib to omit support for openat-related functions having a
182 first argument other than AT_FDCWD. */
183#define GNULIB_SUPPORT_ONLY_AT_FDCWD
184
181#include <string.h> 185#include <string.h>
182#include <stdlib.h> 186#include <stdlib.h>
183 187
diff --git a/src/fileio.c b/src/fileio.c
index b9541e78838..572f6d8ef83 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -2425,15 +2425,7 @@ On Unix, this is a name starting with a `/' or a `~'. */)
2425bool 2425bool
2426check_existing (const char *filename) 2426check_existing (const char *filename)
2427{ 2427{
2428#ifdef DOS_NT 2428 return faccessat (AT_FDCWD, filename, F_OK, AT_EACCESS) == 0;
2429 /* The full emulation of Posix 'stat' is too expensive on
2430 DOS/Windows, when all we want to know is whether the file exists.
2431 So we use 'access' instead, which is much more lightweight. */
2432 return (access (filename, F_OK) >= 0);
2433#else
2434 struct stat st;
2435 return (stat (filename, &st) >= 0);
2436#endif
2437} 2429}
2438 2430
2439/* Return true if file FILENAME exists and can be executed. */ 2431/* Return true if file FILENAME exists and can be executed. */
@@ -2441,56 +2433,40 @@ check_existing (const char *filename)
2441static bool 2433static bool
2442check_executable (char *filename) 2434check_executable (char *filename)
2443{ 2435{
2444#ifdef DOS_NT 2436 return faccessat (AT_FDCWD, filename, X_OK, AT_EACCESS) == 0;
2445 struct stat st;
2446 if (stat (filename, &st) < 0)
2447 return 0;
2448 return ((st.st_mode & S_IEXEC) != 0);
2449#else /* not DOS_NT */
2450#ifdef HAVE_EUIDACCESS
2451 return (euidaccess (filename, 1) >= 0);
2452#else
2453 /* Access isn't quite right because it uses the real uid
2454 and we really want to test with the effective uid.
2455 But Unix doesn't give us a right way to do it. */
2456 return (access (filename, 1) >= 0);
2457#endif
2458#endif /* not DOS_NT */
2459} 2437}
2460 2438
2461/* Return true if file FILENAME exists and can be written. */ 2439/* Return true if file FILENAME exists and can be accessed
2440 according to AMODE, which should include W_OK.
2441 On failure, return false and set errno. */
2462 2442
2463static bool 2443static bool
2464check_writable (const char *filename) 2444check_writable (const char *filename, int amode)
2465{ 2445{
2466#ifdef MSDOS 2446#ifdef MSDOS
2447 /* FIXME: an faccessat implementation should be added to the
2448 DOS/Windows ports and this #ifdef branch should be removed. */
2467 struct stat st; 2449 struct stat st;
2468 if (stat (filename, &st) < 0) 2450 if (stat (filename, &st) < 0)
2469 return 0; 2451 return 0;
2452 errno = EPERM;
2470 return (st.st_mode & S_IWRITE || S_ISDIR (st.st_mode)); 2453 return (st.st_mode & S_IWRITE || S_ISDIR (st.st_mode));
2471#else /* not MSDOS */ 2454#else /* not MSDOS */
2472#ifdef HAVE_EUIDACCESS 2455 bool res = faccessat (AT_FDCWD, filename, amode, AT_EACCESS) == 0;
2473 bool res = (euidaccess (filename, 2) >= 0);
2474#ifdef CYGWIN 2456#ifdef CYGWIN
2475 /* euidaccess may have returned failure because Cygwin couldn't 2457 /* faccessat may have returned failure because Cygwin couldn't
2476 determine the file's UID or GID; if so, we return success. */ 2458 determine the file's UID or GID; if so, we return success. */
2477 if (!res) 2459 if (!res)
2478 { 2460 {
2461 int faccessat_errno = errno;
2479 struct stat st; 2462 struct stat st;
2480 if (stat (filename, &st) < 0) 2463 if (stat (filename, &st) < 0)
2481 return 0; 2464 return 0;
2482 res = (st.st_uid == -1 || st.st_gid == -1); 2465 res = (st.st_uid == -1 || st.st_gid == -1);
2466 errno = faccessat_errno;
2483 } 2467 }
2484#endif /* CYGWIN */ 2468#endif /* CYGWIN */
2485 return res; 2469 return res;
2486#else /* not HAVE_EUIDACCESS */
2487 /* Access isn't quite right because it uses the real uid
2488 and we really want to test with the effective uid.
2489 But Unix doesn't give us a right way to do it.
2490 Opening with O_WRONLY could work for an ordinary file,
2491 but would lose for directories. */
2492 return (access (filename, 2) >= 0);
2493#endif /* not HAVE_EUIDACCESS */
2494#endif /* not MSDOS */ 2470#endif /* not MSDOS */
2495} 2471}
2496 2472
@@ -2547,9 +2523,6 @@ See also `file-exists-p' and `file-attributes'. */)
2547{ 2523{
2548 Lisp_Object absname; 2524 Lisp_Object absname;
2549 Lisp_Object handler; 2525 Lisp_Object handler;
2550 int desc;
2551 int flags;
2552 struct stat statbuf;
2553 2526
2554 CHECK_STRING (filename); 2527 CHECK_STRING (filename);
2555 absname = Fexpand_file_name (filename, Qnil); 2528 absname = Fexpand_file_name (filename, Qnil);
@@ -2561,35 +2534,10 @@ See also `file-exists-p' and `file-attributes'. */)
2561 return call2 (handler, Qfile_readable_p, absname); 2534 return call2 (handler, Qfile_readable_p, absname);
2562 2535
2563 absname = ENCODE_FILE (absname); 2536 absname = ENCODE_FILE (absname);
2564 2537 return (faccessat (AT_FDCWD, SSDATA (absname), R_OK, AT_EACCESS) == 0
2565#if defined (DOS_NT) || defined (macintosh) 2538 ? Qt : Qnil);
2566 /* Under MS-DOS, Windows, and Macintosh, open does not work for
2567 directories. */
2568 if (access (SDATA (absname), 0) == 0)
2569 return Qt;
2570 return Qnil;
2571#else /* not DOS_NT and not macintosh */
2572 flags = O_RDONLY;
2573#ifdef O_NONBLOCK
2574 /* Opening a fifo without O_NONBLOCK can wait.
2575 We don't want to wait. But we don't want to mess wth O_NONBLOCK
2576 except in the case of a fifo, on a system which handles it. */
2577 desc = stat (SSDATA (absname), &statbuf);
2578 if (desc < 0)
2579 return Qnil;
2580 if (S_ISFIFO (statbuf.st_mode))
2581 flags |= O_NONBLOCK;
2582#endif
2583 desc = emacs_open (SSDATA (absname), flags, 0);
2584 if (desc < 0)
2585 return Qnil;
2586 emacs_close (desc);
2587 return Qt;
2588#endif /* not DOS_NT and not macintosh */
2589} 2539}
2590 2540
2591/* Having this before file-symlink-p mysteriously caused it to be forgotten
2592 on the RT/PC. */
2593DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0, 2541DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
2594 doc: /* Return t if file FILENAME can be written or created by you. */) 2542 doc: /* Return t if file FILENAME can be written or created by you. */)
2595 (Lisp_Object filename) 2543 (Lisp_Object filename)
@@ -2607,14 +2555,15 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
2607 return call2 (handler, Qfile_writable_p, absname); 2555 return call2 (handler, Qfile_writable_p, absname);
2608 2556
2609 encoded = ENCODE_FILE (absname); 2557 encoded = ENCODE_FILE (absname);
2610 if (check_existing (SSDATA (encoded))) 2558 if (check_writable (SSDATA (encoded), W_OK))
2611 return (check_writable (SSDATA (encoded)) 2559 return Qt;
2612 ? Qt : Qnil); 2560 if (errno != ENOENT)
2561 return Qnil;
2613 2562
2614 dir = Ffile_name_directory (absname); 2563 dir = Ffile_name_directory (absname);
2564 eassert (!NILP (dir));
2615#ifdef MSDOS 2565#ifdef MSDOS
2616 if (!NILP (dir)) 2566 dir = Fdirectory_file_name (dir);
2617 dir = Fdirectory_file_name (dir);
2618#endif /* MSDOS */ 2567#endif /* MSDOS */
2619 2568
2620 dir = ENCODE_FILE (dir); 2569 dir = ENCODE_FILE (dir);
@@ -2622,10 +2571,9 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
2622 /* The read-only attribute of the parent directory doesn't affect 2571 /* The read-only attribute of the parent directory doesn't affect
2623 whether a file or directory can be created within it. Some day we 2572 whether a file or directory can be created within it. Some day we
2624 should check ACLs though, which do affect this. */ 2573 should check ACLs though, which do affect this. */
2625 return (access (SDATA (dir), D_OK) < 0) ? Qnil : Qt; 2574 return file_directory_p (SDATA (dir)) ? Qt : Qnil;
2626#else 2575#else
2627 return (check_writable (!NILP (dir) ? SSDATA (dir) : "") 2576 return check_writable (SSDATA (dir), W_OK | X_OK) ? Qt : Qnil;
2628 ? Qt : Qnil);
2629#endif 2577#endif
2630} 2578}
2631 2579
@@ -2703,8 +2651,7 @@ Symbolic links to directories count as directories.
2703See `file-symlink-p' to distinguish symlinks. */) 2651See `file-symlink-p' to distinguish symlinks. */)
2704 (Lisp_Object filename) 2652 (Lisp_Object filename)
2705{ 2653{
2706 register Lisp_Object absname; 2654 Lisp_Object absname;
2707 struct stat st;
2708 Lisp_Object handler; 2655 Lisp_Object handler;
2709 2656
2710 absname = expand_and_dir_to_file (filename, BVAR (current_buffer, directory)); 2657 absname = expand_and_dir_to_file (filename, BVAR (current_buffer, directory));
@@ -2717,9 +2664,20 @@ See `file-symlink-p' to distinguish symlinks. */)
2717 2664
2718 absname = ENCODE_FILE (absname); 2665 absname = ENCODE_FILE (absname);
2719 2666
2720 if (stat (SSDATA (absname), &st) < 0) 2667 return file_directory_p (SSDATA (absname)) ? Qt : Qnil;
2721 return Qnil; 2668}
2722 return S_ISDIR (st.st_mode) ? Qt : Qnil; 2669
2670/* Return true if FILE is a directory or a symlink to a directory. */
2671bool
2672file_directory_p (char const *file)
2673{
2674#ifdef WINDOWSNT
2675 /* This is cheaper than 'stat'. */
2676 return faccessat (AT_FDCWD, file, D_OK, AT_EACCESS) == 0;
2677#else
2678 struct stat st;
2679 return stat (file, &st) == 0 && S_ISDIR (st.st_mode);
2680#endif
2723} 2681}
2724 2682
2725DEFUN ("file-accessible-directory-p", Ffile_accessible_directory_p, 2683DEFUN ("file-accessible-directory-p", Ffile_accessible_directory_p,
@@ -2733,21 +2691,65 @@ if the directory so specified exists and really is a readable and
2733searchable directory. */) 2691searchable directory. */)
2734 (Lisp_Object filename) 2692 (Lisp_Object filename)
2735{ 2693{
2694 Lisp_Object absname;
2736 Lisp_Object handler; 2695 Lisp_Object handler;
2737 bool tem; 2696
2738 struct gcpro gcpro1; 2697 CHECK_STRING (filename);
2698 absname = Fexpand_file_name (filename, Qnil);
2739 2699
2740 /* If the file name has special constructs in it, 2700 /* If the file name has special constructs in it,
2741 call the corresponding file handler. */ 2701 call the corresponding file handler. */
2742 handler = Ffind_file_name_handler (filename, Qfile_accessible_directory_p); 2702 handler = Ffind_file_name_handler (absname, Qfile_accessible_directory_p);
2743 if (!NILP (handler)) 2703 if (!NILP (handler))
2744 return call2 (handler, Qfile_accessible_directory_p, filename); 2704 return call2 (handler, Qfile_accessible_directory_p, absname);
2745 2705
2746 GCPRO1 (filename); 2706 absname = ENCODE_FILE (absname);
2747 tem = (NILP (Ffile_directory_p (filename)) 2707 return file_accessible_directory_p (SSDATA (absname)) ? Qt : Qnil;
2748 || NILP (Ffile_executable_p (filename))); 2708}
2749 UNGCPRO; 2709
2750 return tem ? Qnil : Qt; 2710/* If FILE is a searchable directory or a symlink to a
2711 searchable directory, return true. Otherwise return
2712 false and set errno to an error number. */
2713bool
2714file_accessible_directory_p (char const *file)
2715{
2716#ifdef DOS_NT
2717 /* There's no need to test whether FILE is searchable, as the
2718 searchable/executable bit is invented on DOS_NT platforms. */
2719 return file_directory_p (file);
2720#else
2721 /* On POSIXish platforms, use just one system call; this avoids a
2722 race and is typically faster. */
2723 ptrdiff_t len = strlen (file);
2724 char const *dir;
2725 bool ok;
2726 int saved_errno;
2727 USE_SAFE_ALLOCA;
2728
2729 /* Normally a file "FOO" is an accessible directory if "FOO/." exists.
2730 There are three exceptions: "", "/", and "//". Leave "" alone,
2731 as it's invalid. Append only "." to the other two exceptions as
2732 "/" and "//" are distinct on some platforms, whereas "/", "///",
2733 "////", etc. are all equivalent. */
2734 if (! len)
2735 dir = file;
2736 else
2737 {
2738 /* Just check for trailing '/' when deciding whether to append '/'.
2739 That's simpler than testing the two special cases "/" and "//",
2740 and it's a safe optimization here. */
2741 char *buf = SAFE_ALLOCA (len + 3);
2742 memcpy (buf, file, len);
2743 strcpy (buf + len, "/." + (file[len - 1] == '/'));
2744 dir = buf;
2745 }
2746
2747 ok = check_existing (dir);
2748 saved_errno = errno;
2749 SAFE_FREE ();
2750 errno = saved_errno;
2751 return ok;
2752#endif
2751} 2753}
2752 2754
2753DEFUN ("file-regular-p", Ffile_regular_p, Sfile_regular_p, 1, 1, 0, 2755DEFUN ("file-regular-p", Ffile_regular_p, Sfile_regular_p, 1, 1, 0,
@@ -3044,10 +3046,8 @@ Use the current time if TIMESTAMP is nil. TIMESTAMP is in the format of
3044 if (set_file_times (-1, SSDATA (encoded_absname), t, t)) 3046 if (set_file_times (-1, SSDATA (encoded_absname), t, t))
3045 { 3047 {
3046#ifdef MSDOS 3048#ifdef MSDOS
3047 struct stat st;
3048
3049 /* Setting times on a directory always fails. */ 3049 /* Setting times on a directory always fails. */
3050 if (stat (SSDATA (encoded_absname), &st) == 0 && S_ISDIR (st.st_mode)) 3050 if (file_directory_p (SSDATA (encoded_absname)))
3051 return Qnil; 3051 return Qnil;
3052#endif 3052#endif
3053 report_file_error ("Setting file times", Fcons (absname, Qnil)); 3053 report_file_error ("Setting file times", Fcons (absname, Qnil));
diff --git a/src/lisp.h b/src/lisp.h
index 72e38fa4653..67ae28a488f 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3202,6 +3202,8 @@ extern Lisp_Object close_file_unwind (Lisp_Object);
3202extern Lisp_Object restore_point_unwind (Lisp_Object); 3202extern Lisp_Object restore_point_unwind (Lisp_Object);
3203extern _Noreturn void report_file_error (const char *, Lisp_Object); 3203extern _Noreturn void report_file_error (const char *, Lisp_Object);
3204extern void internal_delete_file (Lisp_Object); 3204extern void internal_delete_file (Lisp_Object);
3205extern bool file_directory_p (const char *);
3206extern bool file_accessible_directory_p (const char *);
3205extern void syms_of_fileio (void); 3207extern void syms_of_fileio (void);
3206extern Lisp_Object make_temp_name (Lisp_Object, bool); 3208extern Lisp_Object make_temp_name (Lisp_Object, bool);
3207extern Lisp_Object Qdelete_file; 3209extern Lisp_Object Qdelete_file;
diff --git a/src/lread.c b/src/lread.c
index 3a82e0057e2..5859a2f85a9 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1403,7 +1403,7 @@ Returns the file's name in absolute form, or nil if not found.
1403If SUFFIXES is non-nil, it should be a list of suffixes to append to 1403If SUFFIXES is non-nil, it should be a list of suffixes to append to
1404file name when searching. 1404file name when searching.
1405If non-nil, PREDICATE is used instead of `file-readable-p'. 1405If non-nil, PREDICATE is used instead of `file-readable-p'.
1406PREDICATE can also be an integer to pass to the access(2) function, 1406PREDICATE can also be an integer to pass to the faccessat(2) function,
1407in which case file-name-handlers are ignored. 1407in which case file-name-handlers are ignored.
1408This function will normally skip directories, so if you want it to find 1408This function will normally skip directories, so if you want it to find
1409directories, make sure the PREDICATE function returns `dir-ok' for them. */) 1409directories, make sure the PREDICATE function returns `dir-ok' for them. */)
@@ -1441,7 +1441,6 @@ static Lisp_Object Qdir_ok;
1441int 1441int
1442openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *storeptr, Lisp_Object predicate) 1442openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *storeptr, Lisp_Object predicate)
1443{ 1443{
1444 int fd;
1445 ptrdiff_t fn_size = 100; 1444 ptrdiff_t fn_size = 100;
1446 char buf[100]; 1445 char buf[100];
1447 char *fn = buf; 1446 char *fn = buf;
@@ -1496,7 +1495,6 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *sto
1496 { 1495 {
1497 ptrdiff_t fnlen, lsuffix = SBYTES (XCAR (tail)); 1496 ptrdiff_t fnlen, lsuffix = SBYTES (XCAR (tail));
1498 Lisp_Object handler; 1497 Lisp_Object handler;
1499 bool exists;
1500 1498
1501 /* Concatenate path element/specified name with the suffix. 1499 /* Concatenate path element/specified name with the suffix.
1502 If the directory starts with /:, remove that. */ 1500 If the directory starts with /:, remove that. */
@@ -1520,6 +1518,7 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *sto
1520 handler = Ffind_file_name_handler (string, Qfile_exists_p); 1518 handler = Ffind_file_name_handler (string, Qfile_exists_p);
1521 if ((!NILP (handler) || !NILP (predicate)) && !NATNUMP (predicate)) 1519 if ((!NILP (handler) || !NILP (predicate)) && !NATNUMP (predicate))
1522 { 1520 {
1521 bool exists;
1523 if (NILP (predicate)) 1522 if (NILP (predicate))
1524 exists = !NILP (Ffile_readable_p (string)); 1523 exists = !NILP (Ffile_readable_p (string));
1525 else 1524 else
@@ -1541,37 +1540,40 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *sto
1541 } 1540 }
1542 else 1541 else
1543 { 1542 {
1544#ifndef WINDOWSNT 1543 int fd;
1545 struct stat st;
1546#endif
1547 const char *pfn; 1544 const char *pfn;
1548 1545
1549 encoded_fn = ENCODE_FILE (string); 1546 encoded_fn = ENCODE_FILE (string);
1550 pfn = SSDATA (encoded_fn); 1547 pfn = SSDATA (encoded_fn);
1551#ifdef WINDOWSNT
1552 exists = access (pfn, F_OK) == 0 && access (pfn, D_OK) < 0;
1553#else
1554 exists = (stat (pfn, &st) == 0 && ! S_ISDIR (st.st_mode));
1555#endif
1556 if (exists)
1557 {
1558 /* Check that we can access or open it. */
1559 if (NATNUMP (predicate))
1560 fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
1561 && access (pfn, XFASTINT (predicate)) == 0)
1562 ? 1 : -1);
1563 else
1564 fd = emacs_open (pfn, O_RDONLY, 0);
1565 1548
1566 if (fd >= 0) 1549 /* Check that we can access or open it. */
1550 if (NATNUMP (predicate))
1551 fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
1552 && (faccessat (AT_FDCWD, pfn, XFASTINT (predicate),
1553 AT_EACCESS)
1554 == 0)
1555 && ! file_directory_p (pfn))
1556 ? 1 : -1);
1557 else
1558 {
1559 struct stat st;
1560 fd = emacs_open (pfn, O_RDONLY, 0);
1561 if (0 <= fd
1562 && (fstat (fd, &st) != 0 || S_ISDIR (st.st_mode)))
1567 { 1563 {
1568 /* We succeeded; return this descriptor and filename. */ 1564 emacs_close (fd);
1569 if (storeptr) 1565 fd = -1;
1570 *storeptr = string;
1571 UNGCPRO;
1572 return fd;
1573 } 1566 }
1574 } 1567 }
1568
1569 if (fd >= 0)
1570 {
1571 /* We succeeded; return this descriptor and filename. */
1572 if (storeptr)
1573 *storeptr = string;
1574 UNGCPRO;
1575 return fd;
1576 }
1575 } 1577 }
1576 } 1578 }
1577 if (absolute) 1579 if (absolute)
@@ -4087,9 +4089,8 @@ load_path_check (void)
4087 if (STRINGP (dirfile)) 4089 if (STRINGP (dirfile))
4088 { 4090 {
4089 dirfile = Fdirectory_file_name (dirfile); 4091 dirfile = Fdirectory_file_name (dirfile);
4090 if (access (SSDATA (dirfile), 0) < 0) 4092 if (! file_accessible_directory_p (SSDATA (dirfile)))
4091 dir_warning ("Warning: Lisp directory `%s' does not exist.\n", 4093 dir_warning ("Lisp directory", XCAR (path_tail));
4092 XCAR (path_tail));
4093 } 4094 }
4094 } 4095 }
4095} 4096}
@@ -4201,11 +4202,11 @@ init_lread (void)
4201 Lisp_Object tem, tem1; 4202 Lisp_Object tem, tem1;
4202 4203
4203 /* Add to the path the lisp subdir of the installation 4204 /* Add to the path the lisp subdir of the installation
4204 dir, if it exists. Note: in out-of-tree builds, 4205 dir, if it is accessible. Note: in out-of-tree builds,
4205 this directory is empty save for Makefile. */ 4206 this directory is empty save for Makefile. */
4206 tem = Fexpand_file_name (build_string ("lisp"), 4207 tem = Fexpand_file_name (build_string ("lisp"),
4207 Vinstallation_directory); 4208 Vinstallation_directory);
4208 tem1 = Ffile_exists_p (tem); 4209 tem1 = Ffile_accessible_directory_p (tem);
4209 if (!NILP (tem1)) 4210 if (!NILP (tem1))
4210 { 4211 {
4211 if (NILP (Fmember (tem, Vload_path))) 4212 if (NILP (Fmember (tem, Vload_path)))
@@ -4222,10 +4223,10 @@ init_lread (void)
4222 Lisp dirs instead. */ 4223 Lisp dirs instead. */
4223 Vload_path = nconc2 (Vload_path, dump_path); 4224 Vload_path = nconc2 (Vload_path, dump_path);
4224 4225
4225 /* Add leim under the installation dir, if it exists. */ 4226 /* Add leim under the installation dir, if it is accessible. */
4226 tem = Fexpand_file_name (build_string ("leim"), 4227 tem = Fexpand_file_name (build_string ("leim"),
4227 Vinstallation_directory); 4228 Vinstallation_directory);
4228 tem1 = Ffile_exists_p (tem); 4229 tem1 = Ffile_accessible_directory_p (tem);
4229 if (!NILP (tem1)) 4230 if (!NILP (tem1))
4230 { 4231 {
4231 if (NILP (Fmember (tem, Vload_path))) 4232 if (NILP (Fmember (tem, Vload_path)))
@@ -4237,7 +4238,7 @@ init_lread (void)
4237 { 4238 {
4238 tem = Fexpand_file_name (build_string ("site-lisp"), 4239 tem = Fexpand_file_name (build_string ("site-lisp"),
4239 Vinstallation_directory); 4240 Vinstallation_directory);
4240 tem1 = Ffile_exists_p (tem); 4241 tem1 = Ffile_accessible_directory_p (tem);
4241 if (!NILP (tem1)) 4242 if (!NILP (tem1))
4242 { 4243 {
4243 if (NILP (Fmember (tem, Vload_path))) 4244 if (NILP (Fmember (tem, Vload_path)))
@@ -4282,7 +4283,7 @@ init_lread (void)
4282 { 4283 {
4283 tem = Fexpand_file_name (build_string ("site-lisp"), 4284 tem = Fexpand_file_name (build_string ("site-lisp"),
4284 Vsource_directory); 4285 Vsource_directory);
4285 tem1 = Ffile_exists_p (tem); 4286 tem1 = Ffile_accessible_directory_p (tem);
4286 if (!NILP (tem1)) 4287 if (!NILP (tem1))
4287 { 4288 {
4288 if (NILP (Fmember (tem, Vload_path))) 4289 if (NILP (Fmember (tem, Vload_path)))
@@ -4338,21 +4339,28 @@ init_lread (void)
4338 Vloads_in_progress = Qnil; 4339 Vloads_in_progress = Qnil;
4339} 4340}
4340 4341
4341/* Print a warning, using format string FORMAT, that directory DIRNAME 4342/* Print a warning that directory intended for use USE and with name
4342 does not exist. Print it on stderr and put it in *Messages*. */ 4343 DIRNAME cannot be accessed. On entry, errno should correspond to
4344 the access failure. Print the warning on stderr and put it in
4345 *Messages*. */
4343 4346
4344void 4347void
4345dir_warning (const char *format, Lisp_Object dirname) 4348dir_warning (char const *use, Lisp_Object dirname)
4346{ 4349{
4347 fprintf (stderr, format, SDATA (dirname)); 4350 static char const format[] = "Warning: %s `%s': %s\n";
4351 int access_errno = errno;
4352 fprintf (stderr, format, use, SSDATA (dirname), strerror (access_errno));
4348 4353
4349 /* Don't log the warning before we've initialized!! */ 4354 /* Don't log the warning before we've initialized!! */
4350 if (initialized) 4355 if (initialized)
4351 { 4356 {
4357 char const *diagnostic = emacs_strerror (access_errno);
4352 USE_SAFE_ALLOCA; 4358 USE_SAFE_ALLOCA;
4353 char *buffer = SAFE_ALLOCA (SBYTES (dirname) 4359 char *buffer = SAFE_ALLOCA (sizeof format - 3 * (sizeof "%s" - 1)
4354 + strlen (format) - (sizeof "%s" - 1) + 1); 4360 + strlen (use) + SBYTES (dirname)
4355 ptrdiff_t message_len = esprintf (buffer, format, SDATA (dirname)); 4361 + strlen (diagnostic));
4362 ptrdiff_t message_len = esprintf (buffer, format, use, SSDATA (dirname),
4363 diagnostic);
4356 message_dolog (buffer, message_len, 0, STRING_MULTIBYTE (dirname)); 4364 message_dolog (buffer, message_len, 0, STRING_MULTIBYTE (dirname));
4357 SAFE_FREE (); 4365 SAFE_FREE ();
4358 } 4366 }
diff --git a/src/nsterm.m b/src/nsterm.m
index 7ba1608268b..804ab825dee 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -4112,8 +4112,6 @@ ns_term_init (Lisp_Object display_name)
4112 4112
4113 color_file = Fexpand_file_name (build_string ("rgb.txt"), 4113 color_file = Fexpand_file_name (build_string ("rgb.txt"),
4114 Fsymbol_value (intern ("data-directory"))); 4114 Fsymbol_value (intern ("data-directory")));
4115 if (NILP (Ffile_readable_p (color_file)))
4116 fatal ("Could not find %s.\n", SDATA (color_file));
4117 4115
4118 color_map = Fx_load_color_file (color_file); 4116 color_map = Fx_load_color_file (color_file);
4119 if (NILP (color_map)) 4117 if (NILP (color_map))
diff --git a/src/process.c b/src/process.c
index 43f0239d301..728abebe758 100644
--- a/src/process.c
+++ b/src/process.c
@@ -208,7 +208,7 @@ static EMACS_INT update_tick;
208#ifndef NON_BLOCKING_CONNECT 208#ifndef NON_BLOCKING_CONNECT
209#ifdef HAVE_SELECT 209#ifdef HAVE_SELECT
210#if defined (HAVE_GETPEERNAME) || defined (GNU_LINUX) 210#if defined (HAVE_GETPEERNAME) || defined (GNU_LINUX)
211#if defined (O_NONBLOCK) || defined (O_NDELAY) 211#if O_NONBLOCK || O_NDELAY
212#if defined (EWOULDBLOCK) || defined (EINPROGRESS) 212#if defined (EWOULDBLOCK) || defined (EINPROGRESS)
213#define NON_BLOCKING_CONNECT 213#define NON_BLOCKING_CONNECT
214#endif /* EWOULDBLOCK || EINPROGRESS */ 214#endif /* EWOULDBLOCK || EINPROGRESS */
@@ -655,7 +655,7 @@ allocate_pty (void)
655 PTY_OPEN; 655 PTY_OPEN;
656#else /* no PTY_OPEN */ 656#else /* no PTY_OPEN */
657 { 657 {
658# ifdef O_NONBLOCK 658# if O_NONBLOCK
659 fd = emacs_open (pty_name, O_RDWR | O_NONBLOCK, 0); 659 fd = emacs_open (pty_name, O_RDWR | O_NONBLOCK, 0);
660# else 660# else
661 fd = emacs_open (pty_name, O_RDWR | O_NDELAY, 0); 661 fd = emacs_open (pty_name, O_RDWR | O_NDELAY, 0);
@@ -672,7 +672,7 @@ allocate_pty (void)
672#else 672#else
673 sprintf (pty_name, "/dev/tty%c%x", c, i); 673 sprintf (pty_name, "/dev/tty%c%x", c, i);
674#endif /* no PTY_TTY_NAME_SPRINTF */ 674#endif /* no PTY_TTY_NAME_SPRINTF */
675 if (access (pty_name, 6) != 0) 675 if (faccessat (AT_FDCWD, pty_name, R_OK | W_OK, AT_EACCESS) != 0)
676 { 676 {
677 emacs_close (fd); 677 emacs_close (fd);
678# ifndef __sgi 678# ifndef __sgi
@@ -1624,7 +1624,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
1624#if ! defined (USG) || defined (USG_SUBTTY_WORKS) 1624#if ! defined (USG) || defined (USG_SUBTTY_WORKS)
1625 /* On most USG systems it does not work to open the pty's tty here, 1625 /* On most USG systems it does not work to open the pty's tty here,
1626 then close it and reopen it in the child. */ 1626 then close it and reopen it in the child. */
1627#ifdef O_NOCTTY 1627#if O_NOCTTY
1628 /* Don't let this terminal become our controlling terminal 1628 /* Don't let this terminal become our controlling terminal
1629 (in case we don't have one). */ 1629 (in case we don't have one). */
1630 forkout = forkin = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0); 1630 forkout = forkin = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0);
@@ -1678,11 +1678,11 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
1678 } 1678 }
1679#endif 1679#endif
1680 1680
1681#ifdef O_NONBLOCK 1681#if O_NONBLOCK
1682 fcntl (inchannel, F_SETFL, O_NONBLOCK); 1682 fcntl (inchannel, F_SETFL, O_NONBLOCK);
1683 fcntl (outchannel, F_SETFL, O_NONBLOCK); 1683 fcntl (outchannel, F_SETFL, O_NONBLOCK);
1684#else 1684#else
1685#ifdef O_NDELAY 1685#if O_NDELAY
1686 fcntl (inchannel, F_SETFL, O_NDELAY); 1686 fcntl (inchannel, F_SETFL, O_NDELAY);
1687 fcntl (outchannel, F_SETFL, O_NDELAY); 1687 fcntl (outchannel, F_SETFL, O_NDELAY);
1688#endif 1688#endif
@@ -1943,7 +1943,7 @@ create_pty (Lisp_Object process)
1943#if ! defined (USG) || defined (USG_SUBTTY_WORKS) 1943#if ! defined (USG) || defined (USG_SUBTTY_WORKS)
1944 /* On most USG systems it does not work to open the pty's tty here, 1944 /* On most USG systems it does not work to open the pty's tty here,
1945 then close it and reopen it in the child. */ 1945 then close it and reopen it in the child. */
1946#ifdef O_NOCTTY 1946#if O_NOCTTY
1947 /* Don't let this terminal become our controlling terminal 1947 /* Don't let this terminal become our controlling terminal
1948 (in case we don't have one). */ 1948 (in case we don't have one). */
1949 int forkout = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0); 1949 int forkout = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0);
@@ -1963,11 +1963,11 @@ create_pty (Lisp_Object process)
1963 } 1963 }
1964#endif /* HAVE_PTYS */ 1964#endif /* HAVE_PTYS */
1965 1965
1966#ifdef O_NONBLOCK 1966#if O_NONBLOCK
1967 fcntl (inchannel, F_SETFL, O_NONBLOCK); 1967 fcntl (inchannel, F_SETFL, O_NONBLOCK);
1968 fcntl (outchannel, F_SETFL, O_NONBLOCK); 1968 fcntl (outchannel, F_SETFL, O_NONBLOCK);
1969#else 1969#else
1970#ifdef O_NDELAY 1970#if O_NDELAY
1971 fcntl (inchannel, F_SETFL, O_NDELAY); 1971 fcntl (inchannel, F_SETFL, O_NDELAY);
1972 fcntl (outchannel, F_SETFL, O_NDELAY); 1972 fcntl (outchannel, F_SETFL, O_NDELAY);
1973#endif 1973#endif
@@ -2927,7 +2927,7 @@ usage: (make-network-process &rest ARGS) */)
2927 { 2927 {
2928 /* Don't support network sockets when non-blocking mode is 2928 /* Don't support network sockets when non-blocking mode is
2929 not available, since a blocked Emacs is not useful. */ 2929 not available, since a blocked Emacs is not useful. */
2930#if !defined (O_NONBLOCK) && !defined (O_NDELAY) 2930#if !O_NONBLOCK && !O_NDELAY
2931 error ("Network servers not supported"); 2931 error ("Network servers not supported");
2932#else 2932#else
2933 is_server = 1; 2933 is_server = 1;
@@ -3193,7 +3193,7 @@ usage: (make-network-process &rest ARGS) */)
3193#ifdef NON_BLOCKING_CONNECT 3193#ifdef NON_BLOCKING_CONNECT
3194 if (is_non_blocking_client) 3194 if (is_non_blocking_client)
3195 { 3195 {
3196#ifdef O_NONBLOCK 3196#if O_NONBLOCK
3197 ret = fcntl (s, F_SETFL, O_NONBLOCK); 3197 ret = fcntl (s, F_SETFL, O_NONBLOCK);
3198#else 3198#else
3199 ret = fcntl (s, F_SETFL, O_NDELAY); 3199 ret = fcntl (s, F_SETFL, O_NDELAY);
@@ -3410,10 +3410,10 @@ usage: (make-network-process &rest ARGS) */)
3410 3410
3411 chan_process[inch] = proc; 3411 chan_process[inch] = proc;
3412 3412
3413#ifdef O_NONBLOCK 3413#if O_NONBLOCK
3414 fcntl (inch, F_SETFL, O_NONBLOCK); 3414 fcntl (inch, F_SETFL, O_NONBLOCK);
3415#else 3415#else
3416#ifdef O_NDELAY 3416#if O_NDELAY
3417 fcntl (inch, F_SETFL, O_NDELAY); 3417 fcntl (inch, F_SETFL, O_NDELAY);
3418#endif 3418#endif
3419#endif 3419#endif
@@ -4145,10 +4145,10 @@ server_accept_connection (Lisp_Object server, int channel)
4145 4145
4146 chan_process[s] = proc; 4146 chan_process[s] = proc;
4147 4147
4148#ifdef O_NONBLOCK 4148#if O_NONBLOCK
4149 fcntl (s, F_SETFL, O_NONBLOCK); 4149 fcntl (s, F_SETFL, O_NONBLOCK);
4150#else 4150#else
4151#ifdef O_NDELAY 4151#if O_NDELAY
4152 fcntl (s, F_SETFL, O_NDELAY); 4152 fcntl (s, F_SETFL, O_NDELAY);
4153#endif 4153#endif
4154#endif 4154#endif
@@ -4849,11 +4849,11 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
4849#endif 4849#endif
4850 /* ISC 4.1 defines both EWOULDBLOCK and O_NONBLOCK, 4850 /* ISC 4.1 defines both EWOULDBLOCK and O_NONBLOCK,
4851 and Emacs uses O_NONBLOCK, so what we get is EAGAIN. */ 4851 and Emacs uses O_NONBLOCK, so what we get is EAGAIN. */
4852#ifdef O_NONBLOCK 4852#if O_NONBLOCK
4853 else if (nread == -1 && errno == EAGAIN) 4853 else if (nread == -1 && errno == EAGAIN)
4854 ; 4854 ;
4855#else 4855#else
4856#ifdef O_NDELAY 4856#if O_NDELAY
4857 else if (nread == -1 && errno == EAGAIN) 4857 else if (nread == -1 && errno == EAGAIN)
4858 ; 4858 ;
4859 /* Note that we cannot distinguish between no input 4859 /* Note that we cannot distinguish between no input
@@ -7348,7 +7348,7 @@ init_process_emacs (void)
7348#ifdef HAVE_GETSOCKNAME 7348#ifdef HAVE_GETSOCKNAME
7349 ADD_SUBFEATURE (QCservice, Qt); 7349 ADD_SUBFEATURE (QCservice, Qt);
7350#endif 7350#endif
7351#if defined (O_NONBLOCK) || defined (O_NDELAY) 7351#if O_NONBLOCK || O_NDELAY
7352 ADD_SUBFEATURE (QCserver, Qt); 7352 ADD_SUBFEATURE (QCserver, Qt);
7353#endif 7353#endif
7354 7354
diff --git a/src/sysdep.c b/src/sysdep.c
index aa9d0f38c3c..a7f3de2f1b1 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -1287,7 +1287,7 @@ reset_sys_modes (struct tty_display_info *tty_out)
1287 old_fcntl_owner[fileno (tty_out->input)]); 1287 old_fcntl_owner[fileno (tty_out->input)]);
1288 } 1288 }
1289#endif /* F_SETOWN */ 1289#endif /* F_SETOWN */
1290#ifdef O_NDELAY 1290#if O_NDELAY
1291 fcntl (fileno (tty_out->input), F_SETFL, 1291 fcntl (fileno (tty_out->input), F_SETFL,
1292 fcntl (fileno (tty_out->input), F_GETFL, 0) & ~O_NDELAY); 1292 fcntl (fileno (tty_out->input), F_GETFL, 0) & ~O_NDELAY);
1293#endif 1293#endif
@@ -2384,12 +2384,12 @@ serial_open (char *port)
2384 2384
2385 fd = emacs_open ((char*) port, 2385 fd = emacs_open ((char*) port,
2386 O_RDWR 2386 O_RDWR
2387#ifdef O_NONBLOCK 2387#if O_NONBLOCK
2388 | O_NONBLOCK 2388 | O_NONBLOCK
2389#else 2389#else
2390 | O_NDELAY 2390 | O_NDELAY
2391#endif 2391#endif
2392#ifdef O_NOCTTY 2392#if O_NOCTTY
2393 | O_NOCTTY 2393 | O_NOCTTY
2394#endif 2394#endif
2395 , 0); 2395 , 0);
diff --git a/src/term.c b/src/term.c
index 578c701858f..96549290da5 100644
--- a/src/term.c
+++ b/src/term.c
@@ -2992,7 +2992,7 @@ init_tty (const char *name, const char *terminal_type, int must_succeed)
2992 int fd; 2992 int fd;
2993 FILE *file; 2993 FILE *file;
2994 2994
2995#ifdef O_IGNORE_CTTY 2995#if O_IGNORE_CTTY
2996 if (!ctty) 2996 if (!ctty)
2997 /* Open the terminal device. Don't recognize it as our 2997 /* Open the terminal device. Don't recognize it as our
2998 controlling terminal, and don't make it the controlling tty 2998 controlling terminal, and don't make it the controlling tty
@@ -3023,7 +3023,7 @@ init_tty (const char *name, const char *terminal_type, int must_succeed)
3023 name); 3023 name);
3024 } 3024 }
3025 3025
3026#ifndef O_IGNORE_CTTY 3026#if !O_IGNORE_CTTY
3027 if (!ctty) 3027 if (!ctty)
3028 dissociate_if_controlling_tty (fd); 3028 dissociate_if_controlling_tty (fd);
3029#endif 3029#endif
diff --git a/src/w32.c b/src/w32.c
index 5ac1bc3eb7c..0e7da449b81 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -1597,7 +1597,7 @@ init_environment (char ** argv)
1597 see if it succeeds. But I think that's too much to ask. */ 1597 see if it succeeds. But I think that's too much to ask. */
1598 1598
1599 /* MSVCRT's _access crashes with D_OK. */ 1599 /* MSVCRT's _access crashes with D_OK. */
1600 if (tmp && sys_access (tmp, D_OK) == 0) 1600 if (tmp && sys_faccessat (AT_FDCWD, tmp, D_OK, AT_EACCESS) == 0)
1601 { 1601 {
1602 char * var = alloca (strlen (tmp) + 8); 1602 char * var = alloca (strlen (tmp) + 8);
1603 sprintf (var, "TMPDIR=%s", tmp); 1603 sprintf (var, "TMPDIR=%s", tmp);
@@ -2714,10 +2714,16 @@ logon_network_drive (const char *path)
2714 long file names. */ 2714 long file names. */
2715 2715
2716int 2716int
2717sys_access (const char * path, int mode) 2717sys_faccessat (int dirfd, const char * path, int mode, int flags)
2718{ 2718{
2719 DWORD attributes; 2719 DWORD attributes;
2720 2720
2721 if (dirfd != AT_FDCWD)
2722 {
2723 errno = EBADF;
2724 return -1;
2725 }
2726
2721 /* MSVCRT implementation of 'access' doesn't recognize D_OK, and its 2727 /* MSVCRT implementation of 'access' doesn't recognize D_OK, and its
2722 newer versions blow up when passed D_OK. */ 2728 newer versions blow up when passed D_OK. */
2723 path = map_w32_filename (path, NULL); 2729 path = map_w32_filename (path, NULL);
@@ -2960,7 +2966,7 @@ sys_mktemp (char * template)
2960 { 2966 {
2961 int save_errno = errno; 2967 int save_errno = errno;
2962 p[0] = first_char[i]; 2968 p[0] = first_char[i];
2963 if (sys_access (template, 0) < 0) 2969 if (sys_faccessat (AT_FDCWD, template, F_OK, AT_EACCESS) < 0)
2964 { 2970 {
2965 errno = save_errno; 2971 errno = save_errno;
2966 return template; 2972 return template;
@@ -4011,7 +4017,7 @@ symlink (char const *filename, char const *linkname)
4011 { 4017 {
4012 /* Non-absolute FILENAME is understood as being relative to 4018 /* Non-absolute FILENAME is understood as being relative to
4013 LINKNAME's directory. We need to prepend that directory to 4019 LINKNAME's directory. We need to prepend that directory to
4014 FILENAME to get correct results from sys_access below, since 4020 FILENAME to get correct results from sys_faccessat below, since
4015 otherwise it will interpret FILENAME relative to the 4021 otherwise it will interpret FILENAME relative to the
4016 directory where the Emacs process runs. Note that 4022 directory where the Emacs process runs. Note that
4017 make-symbolic-link always makes sure LINKNAME is a fully 4023 make-symbolic-link always makes sure LINKNAME is a fully
@@ -4025,10 +4031,10 @@ symlink (char const *filename, char const *linkname)
4025 strncpy (tem, linkfn, p - linkfn); 4031 strncpy (tem, linkfn, p - linkfn);
4026 tem[p - linkfn] = '\0'; 4032 tem[p - linkfn] = '\0';
4027 strcat (tem, filename); 4033 strcat (tem, filename);
4028 dir_access = sys_access (tem, D_OK); 4034 dir_access = sys_faccessat (AT_FDCWD, tem, D_OK, AT_EACCESS);
4029 } 4035 }
4030 else 4036 else
4031 dir_access = sys_access (filename, D_OK); 4037 dir_access = sys_faccessat (AT_FDCWD, filename, D_OK, AT_EACCESS);
4032 4038
4033 /* Since Windows distinguishes between symlinks to directories and 4039 /* Since Windows distinguishes between symlinks to directories and
4034 to files, we provide a kludgy feature: if FILENAME doesn't 4040 to files, we provide a kludgy feature: if FILENAME doesn't
diff --git a/src/xrdb.c b/src/xrdb.c
index 9d056a607e4..59b0876ebf8 100644
--- a/src/xrdb.c
+++ b/src/xrdb.c
@@ -41,7 +41,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
41#ifdef HAVE_PWD_H 41#ifdef HAVE_PWD_H
42#include <pwd.h> 42#include <pwd.h>
43#endif 43#endif
44#include <sys/stat.h>
45 44
46#ifdef USE_MOTIF 45#ifdef USE_MOTIF
47/* For Vdouble_click_time. */ 46/* For Vdouble_click_time. */
@@ -50,7 +49,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
50 49
51char *x_get_string_resource (XrmDatabase rdb, const char *name, 50char *x_get_string_resource (XrmDatabase rdb, const char *name,
52 const char *class); 51 const char *class);
53static int file_p (const char *filename);
54 52
55 53
56/* X file search path processing. */ 54/* X file search path processing. */
@@ -108,7 +106,7 @@ x_get_customization_string (XrmDatabase db, const char *name,
108 database associated with display. 106 database associated with display.
109 (This is x_customization_string.) 107 (This is x_customization_string.)
110 108
111 Return the expanded file name if it exists and is readable, and 109 Return the resource database if its file was read successfully, and
112 refers to %L only when the LANG environment variable is set, or 110 refers to %L only when the LANG environment variable is set, or
113 otherwise provided by X. 111 otherwise provided by X.
114 112
@@ -117,10 +115,11 @@ x_get_customization_string (XrmDatabase db, const char *name,
117 115
118 Return NULL otherwise. */ 116 Return NULL otherwise. */
119 117
120static char * 118static XrmDatabase
121magic_file_p (const char *string, ptrdiff_t string_len, const char *class, 119magic_db (const char *string, ptrdiff_t string_len, const char *class,
122 const char *escaped_suffix) 120 const char *escaped_suffix)
123{ 121{
122 XrmDatabase db;
124 char *lang = getenv ("LANG"); 123 char *lang = getenv ("LANG");
125 124
126 ptrdiff_t path_size = 100; 125 ptrdiff_t path_size = 100;
@@ -217,14 +216,9 @@ magic_file_p (const char *string, ptrdiff_t string_len, const char *class,
217 } 216 }
218 217
219 path[path_len] = '\0'; 218 path[path_len] = '\0';
220 219 db = XrmGetFileDatabase (path);
221 if (! file_p (path)) 220 xfree (path);
222 { 221 return db;
223 xfree (path);
224 return NULL;
225 }
226
227 return path;
228} 222}
229 223
230 224
@@ -258,22 +252,11 @@ gethomedir (void)
258} 252}
259 253
260 254
261static int
262file_p (const char *filename)
263{
264 struct stat status;
265
266 return (access (filename, 4) == 0 /* exists and is readable */
267 && stat (filename, &status) == 0 /* get the status */
268 && (S_ISDIR (status.st_mode)) == 0); /* not a directory */
269}
270
271
272/* Find the first element of SEARCH_PATH which exists and is readable, 255/* Find the first element of SEARCH_PATH which exists and is readable,
273 after expanding the %-escapes. Return 0 if we didn't find any, and 256 after expanding the %-escapes. Return 0 if we didn't find any, and
274 the path name of the one we found otherwise. */ 257 the path name of the one we found otherwise. */
275 258
276static char * 259static XrmDatabase
277search_magic_path (const char *search_path, const char *class, 260search_magic_path (const char *search_path, const char *class,
278 const char *escaped_suffix) 261 const char *escaped_suffix)
279{ 262{
@@ -286,18 +269,16 @@ search_magic_path (const char *search_path, const char *class,
286 269
287 if (p > s) 270 if (p > s)
288 { 271 {
289 char *path = magic_file_p (s, p - s, class, escaped_suffix); 272 XrmDatabase db = magic_db (s, p - s, class, escaped_suffix);
290 if (path) 273 if (db)
291 return path; 274 return db;
292 } 275 }
293 else if (*p == ':') 276 else if (*p == ':')
294 { 277 {
295 char *path; 278 static char const ns[] = "%N%S";
296 279 XrmDatabase db = magic_db (ns, strlen (ns), class, escaped_suffix);
297 s = "%N%S"; 280 if (db)
298 path = magic_file_p (s, strlen (s), class, escaped_suffix); 281 return db;
299 if (path)
300 return path;
301 } 282 }
302 283
303 if (*p == ':') 284 if (*p == ':')
@@ -312,21 +293,12 @@ search_magic_path (const char *search_path, const char *class,
312static XrmDatabase 293static XrmDatabase
313get_system_app (const char *class) 294get_system_app (const char *class)
314{ 295{
315 XrmDatabase db = NULL;
316 const char *path; 296 const char *path;
317 char *p;
318 297
319 path = getenv ("XFILESEARCHPATH"); 298 path = getenv ("XFILESEARCHPATH");
320 if (! path) path = PATH_X_DEFAULTS; 299 if (! path) path = PATH_X_DEFAULTS;
321 300
322 p = search_magic_path (path, class, 0); 301 return search_magic_path (path, class, 0);
323 if (p)
324 {
325 db = XrmGetFileDatabase (p);
326 xfree (p);
327 }
328
329 return db;
330} 302}
331 303
332 304
@@ -340,35 +312,40 @@ get_fallback (Display *display)
340static XrmDatabase 312static XrmDatabase
341get_user_app (const char *class) 313get_user_app (const char *class)
342{ 314{
315 XrmDatabase db = 0;
343 const char *path; 316 const char *path;
344 char *file = 0;
345 char *free_it = 0;
346 317
347 /* Check for XUSERFILESEARCHPATH. It is a path of complete file 318 /* Check for XUSERFILESEARCHPATH. It is a path of complete file
348 names, not directories. */ 319 names, not directories. */
349 if (((path = getenv ("XUSERFILESEARCHPATH")) 320 path = getenv ("XUSERFILESEARCHPATH");
350 && (file = search_magic_path (path, class, 0))) 321 if (path)
322 db = search_magic_path (path, class, 0);
351 323
324 if (! db)
325 {
352 /* Check for APPLRESDIR; it is a path of directories. In each, 326 /* Check for APPLRESDIR; it is a path of directories. In each,
353 we have to search for LANG/CLASS and then CLASS. */ 327 we have to search for LANG/CLASS and then CLASS. */
354 || ((path = getenv ("XAPPLRESDIR")) 328 path = getenv ("XAPPLRESDIR");
355 && ((file = search_magic_path (path, class, "/%L/%N")) 329 if (path)
356 || (file = search_magic_path (path, class, "/%N")))) 330 {
331 db = search_magic_path (path, class, "/%L/%N");
332 if (!db)
333 db = search_magic_path (path, class, "/%N");
334 }
335 }
357 336
337 if (! db)
338 {
358 /* Check in the home directory. This is a bit of a hack; let's 339 /* Check in the home directory. This is a bit of a hack; let's
359 hope one's home directory doesn't contain any %-escapes. */ 340 hope one's home directory doesn't contain any %-escapes. */
360 || (free_it = gethomedir (), 341 char *home = gethomedir ();
361 ((file = search_magic_path (free_it, class, "%L/%N")) 342 db = search_magic_path (home, class, "%L/%N");
362 || (file = search_magic_path (free_it, class, "%N"))))) 343 if (! db)
363 { 344 db = search_magic_path (home, class, "%N");
364 XrmDatabase db = XrmGetFileDatabase (file); 345 xfree (home);
365 xfree (file);
366 xfree (free_it);
367 return db;
368 } 346 }
369 347
370 xfree (free_it); 348 return db;
371 return NULL;
372} 349}
373 350
374 351