aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2019-09-17 19:18:14 -0700
committerPaul Eggert2019-09-17 19:24:38 -0700
commit9dc306b1db08196684d05a474148e16305adbad0 (patch)
tree78a401e0156a34ef1d2ae99acad31fb3ad9cb806 /src
parentae3edf0ac3f1e893338917497b55859d6aca7d42 (diff)
downloademacs-9dc306b1db08196684d05a474148e16305adbad0.tar.gz
emacs-9dc306b1db08196684d05a474148e16305adbad0.zip
Improve reporting of I/O, access errors
Signal an error for file-oriented errors that are not tame errors like ENOENT and ENOTDIR (Bug#37389). Do this for primitives exposed to Lisp; the lower level internal C API merely makes errno values available to higher-level C code. * doc/lispref/files.texi (Testing Accessibility) (File Attributes, Extended Attributes): Do not say that the functions return nil when the return value cannot be determined. * etc/NEWS: Mention the change. * src/dired.c (Ffile_attributes): Fix doc string confusion about opening a file vs getting its attributes. (file_attributes): Signal serious errors. * src/fileio.c (check_existing, check_executable) (check_writable): Remove. All callers changed to use check_file_access or file_access_p. (file_access_p, file_metadata_errno, file_attribute_errno) (file_test_errno, check_file_access, check_emacs_readlinkat): New functions. * src/fileio.c (Ffile_executable_p, Ffile_readable_p) (Ffile_name_case_insensitive_p, Frename_file, Ffile_exists_p): (Ffile_symlink_p, Ffile_directory_p) (Ffile_accessible_directory_p, Ffile_regular_p) (Ffile_selinux_context, Ffile_acl, Ffile_modes) (Ffile_newer_than_file_p, Fset_visited_file_modtime) (Ffile_system_info): * src/filelock.c (unlock_file, Ffile_locked_p): * src/lread.c (Fload): Signal serious errors. * src/fileio.c (Ffile_writable_p): Remove unnecessary CHECK_STRING. (emacs_readlinkat): Now static. * src/filelock.c (current_lock_owner, lock_if_free): Return a positive errno on error, and the negative of the old old value on success. All callers changed. * src/lread.c (openp): Propagate serious errno values to caller.
Diffstat (limited to 'src')
-rw-r--r--src/dired.c9
-rw-r--r--src/emacs.c2
-rw-r--r--src/fileio.c359
-rw-r--r--src/filelock.c86
-rw-r--r--src/lisp.h5
-rw-r--r--src/lread.c21
6 files changed, 267 insertions, 215 deletions
diff --git a/src/dired.c b/src/dired.c
index df03bc32cef..3768b6dbb7c 100644
--- a/src/dired.c
+++ b/src/dired.c
@@ -819,7 +819,7 @@ stat_gname (struct stat *st)
819 819
820DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 2, 0, 820DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 2, 0,
821 doc: /* Return a list of attributes of file FILENAME. 821 doc: /* Return a list of attributes of file FILENAME.
822Value is nil if specified file cannot be opened. 822Value is nil if specified file does not exist.
823 823
824ID-FORMAT specifies the preferred format of attributes uid and gid (see 824ID-FORMAT specifies the preferred format of attributes uid and gid (see
825below) - valid values are `string' and `integer'. The latter is the 825below) - valid values are `string' and `integer'. The latter is the
@@ -939,15 +939,14 @@ file_attributes (int fd, char const *name,
939 information to be accurate. */ 939 information to be accurate. */
940 w32_stat_get_owner_group = 1; 940 w32_stat_get_owner_group = 1;
941#endif 941#endif
942 if (fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) 942 err = fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW) == 0 ? 0 : errno;
943 err = 0;
944#ifdef WINDOWSNT 943#ifdef WINDOWSNT
945 w32_stat_get_owner_group = 0; 944 w32_stat_get_owner_group = 0;
946#endif 945#endif
947 } 946 }
948 947
949 if (err != 0) 948 if (err != 0)
950 return unbind_to (count, Qnil); 949 return unbind_to (count, file_attribute_errno (filename, err));
951 950
952 Lisp_Object file_type; 951 Lisp_Object file_type;
953 if (S_ISLNK (s.st_mode)) 952 if (S_ISLNK (s.st_mode))
@@ -956,7 +955,7 @@ file_attributes (int fd, char const *name,
956 symlink is replaced between the call to fstatat and the call 955 symlink is replaced between the call to fstatat and the call
957 to emacs_readlinkat. Detect this race unless the replacement 956 to emacs_readlinkat. Detect this race unless the replacement
958 is also a symlink. */ 957 is also a symlink. */
959 file_type = emacs_readlinkat (fd, name); 958 file_type = check_emacs_readlinkat (fd, filename, name);
960 if (NILP (file_type)) 959 if (NILP (file_type))
961 return unbind_to (count, Qnil); 960 return unbind_to (count, Qnil);
962 } 961 }
diff --git a/src/emacs.c b/src/emacs.c
index 558dd11a351..eb732810db4 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -746,7 +746,7 @@ load_pdump_find_executable (char const *argv0, ptrdiff_t *candidate_size)
746 candidate[path_part_length] = DIRECTORY_SEP; 746 candidate[path_part_length] = DIRECTORY_SEP;
747 memcpy (candidate + path_part_length + 1, argv0, argv0_length + 1); 747 memcpy (candidate + path_part_length + 1, argv0, argv0_length + 1);
748 struct stat st; 748 struct stat st;
749 if (check_executable (candidate) 749 if (file_access_p (candidate, X_OK)
750 && stat (candidate, &st) == 0 && S_ISREG (st.st_mode)) 750 && stat (candidate, &st) == 0 && S_ISREG (st.st_mode))
751 return candidate; 751 return candidate;
752 *candidate = '\0'; 752 *candidate = '\0';
diff --git a/src/fileio.c b/src/fileio.c
index 81c29ca0cca..0977516f019 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -141,54 +141,38 @@ static bool e_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
141 struct coding_system *); 141 struct coding_system *);
142 142
143 143
144/* Return true if FILENAME exists, otherwise return false and set errno. */ 144/* Test whether FILE is accessible for AMODE.
145 145 Return true if successful, false (setting errno) otherwise. */
146static bool
147check_existing (const char *filename)
148{
149 return faccessat (AT_FDCWD, filename, F_OK, AT_EACCESS) == 0;
150}
151
152/* Return true if file FILENAME exists and can be executed. */
153 146
154bool 147bool
155check_executable (char *filename) 148file_access_p (char const *file, int amode)
156{
157 return faccessat (AT_FDCWD, filename, X_OK, AT_EACCESS) == 0;
158}
159
160/* Return true if file FILENAME exists and can be accessed
161 according to AMODE, which should include W_OK.
162 On failure, return false and set errno. */
163
164static bool
165check_writable (const char *filename, int amode)
166{ 149{
167#ifdef MSDOS 150#ifdef MSDOS
168 /* FIXME: an faccessat implementation should be added to the 151 if (amode & W_OK)
169 DOS/Windows ports and this #ifdef branch should be removed. */
170 struct stat st;
171 if (stat (filename, &st) < 0)
172 return 0;
173 errno = EPERM;
174 return (st.st_mode & S_IWRITE || S_ISDIR (st.st_mode));
175#else /* not MSDOS */
176 bool res = faccessat (AT_FDCWD, filename, amode, AT_EACCESS) == 0;
177#ifdef CYGWIN
178 /* faccessat may have returned failure because Cygwin couldn't
179 determine the file's UID or GID; if so, we return success. */
180 if (!res)
181 { 152 {
182 int faccessat_errno = errno; 153 /* FIXME: The MS-DOS faccessat implementation should handle this. */
183 struct stat st; 154 struct stat st;
184 if (stat (filename, &st) < 0) 155 if (stat (file, &st) != 0)
185 return 0; 156 return false;
186 res = (st.st_uid == -1 || st.st_gid == -1); 157 errno = EPERM;
187 errno = faccessat_errno; 158 return st.st_mode & S_IWRITE || S_ISDIR (st.st_mode);
188 } 159 }
189#endif /* CYGWIN */ 160#endif
190 return res; 161
191#endif /* not MSDOS */ 162 if (faccessat (AT_FDCWD, file, amode, AT_EACCESS) == 0)
163 return true;
164
165#ifdef CYGWIN
166 /* Return success if faccessat failed because Cygwin couldn't
167 determine the file's UID or GID. */
168 int err = errno;
169 struct stat st;
170 if (stat (file, &st) == 0 && (st.st_uid == -1 || st.st_gid == -1))
171 return true;
172 errno = err;
173#endif
174
175 return false;
192} 176}
193 177
194/* Signal a file-access failure. STRING describes the failure, 178/* Signal a file-access failure. STRING describes the failure,
@@ -251,6 +235,30 @@ report_file_notify_error (const char *string, Lisp_Object name)
251} 235}
252#endif 236#endif
253 237
238/* ACTION failed for FILE with errno ERR. Signal an error if ERR
239 means the file's metadata could not be retrieved even though it may
240 exist, otherwise return nil. */
241
242static Lisp_Object
243file_metadata_errno (char const *action, Lisp_Object file, int err)
244{
245 if (err == ENOENT || err == ENOTDIR || err == 0)
246 return Qnil;
247 report_file_errno (action, file, err);
248}
249
250Lisp_Object
251file_attribute_errno (Lisp_Object file, int err)
252{
253 return file_metadata_errno ("Getting attributes", file, err);
254}
255
256static Lisp_Object
257file_test_errno (Lisp_Object file, int err)
258{
259 return file_metadata_errno ("Testing file", file, err);
260}
261
254void 262void
255close_file_unwind (int fd) 263close_file_unwind (int fd)
256{ 264{
@@ -2446,8 +2454,12 @@ The arg must be a string. */)
2446 while (true) 2454 while (true)
2447 { 2455 {
2448 int err = file_name_case_insensitive_err (filename); 2456 int err = file_name_case_insensitive_err (filename);
2449 if (! (err == ENOENT || err == ENOTDIR)) 2457 switch (err)
2450 return err < 0 ? Qt : Qnil; 2458 {
2459 case -1: return Qt;
2460 default: return file_test_errno (filename, err);
2461 case ENOENT: case ENOTDIR: break;
2462 }
2451 Lisp_Object parent = file_name_directory (filename); 2463 Lisp_Object parent = file_name_directory (filename);
2452 /* Avoid infinite loop if the root is reported as non-existing 2464 /* Avoid infinite loop if the root is reported as non-existing
2453 (impossible?). */ 2465 (impossible?). */
@@ -2560,7 +2572,7 @@ This is what happens in interactive use with M-x. */)
2560 { 2572 {
2561 Lisp_Object symlink_target 2573 Lisp_Object symlink_target
2562 = (S_ISLNK (file_st.st_mode) 2574 = (S_ISLNK (file_st.st_mode)
2563 ? emacs_readlinkat (AT_FDCWD, SSDATA (encoded_file)) 2575 ? check_emacs_readlinkat (AT_FDCWD, file, SSDATA (encoded_file))
2564 : Qnil); 2576 : Qnil);
2565 if (!NILP (symlink_target)) 2577 if (!NILP (symlink_target))
2566 Fmake_symbolic_link (symlink_target, newname, ok_if_already_exists); 2578 Fmake_symbolic_link (symlink_target, newname, ok_if_already_exists);
@@ -2708,32 +2720,48 @@ file_name_absolute_p (char const *filename)
2708 || user_homedir (&filename[1])))); 2720 || user_homedir (&filename[1]))));
2709} 2721}
2710 2722
2711DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0, 2723/* Return t if FILE exists and is accessible via OPERATION and AMODE,
2712 doc: /* Return t if file FILENAME exists (whether or not you can read it). 2724 nil (setting errno) if not. Signal an error if the result cannot
2713See also `file-readable-p' and `file-attributes'. 2725 be determined. */
2714This returns nil for a symlink to a nonexistent file.
2715Use `file-symlink-p' to test for such links. */)
2716 (Lisp_Object filename)
2717{
2718 Lisp_Object absname;
2719 Lisp_Object handler;
2720 2726
2721 CHECK_STRING (filename); 2727static Lisp_Object
2722 absname = Fexpand_file_name (filename, Qnil); 2728check_file_access (Lisp_Object file, Lisp_Object operation, int amode)
2723 2729{
2724 /* If the file name has special constructs in it, 2730 file = Fexpand_file_name (file, Qnil);
2725 call the corresponding file name handler. */ 2731 Lisp_Object handler = Ffind_file_name_handler (file, operation);
2726 handler = Ffind_file_name_handler (absname, Qfile_exists_p);
2727 if (!NILP (handler)) 2732 if (!NILP (handler))
2728 { 2733 {
2729 Lisp_Object result = call2 (handler, Qfile_exists_p, absname); 2734 Lisp_Object ok = call2 (handler, operation, file);
2735 /* This errno value is bogus. Any caller that depends on errno
2736 should be rethought anyway, to avoid a race between testing a
2737 handled file's accessibility and using the file. */
2730 errno = 0; 2738 errno = 0;
2731 return result; 2739 return ok;
2732 } 2740 }
2733 2741
2734 absname = ENCODE_FILE (absname); 2742 char *encoded_file = SSDATA (ENCODE_FILE (file));
2743 bool ok = file_access_p (encoded_file, amode);
2744 if (ok)
2745 return Qt;
2746 int err = errno;
2747 if (err == EROFS || err == ETXTBSY
2748 || (err == EACCES && amode != F_OK
2749 && file_access_p (encoded_file, F_OK)))
2750 {
2751 errno = err;
2752 return Qnil;
2753 }
2754 return file_test_errno (file, err);
2755}
2735 2756
2736 return check_existing (SSDATA (absname)) ? Qt : Qnil; 2757DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0,
2758 doc: /* Return t if file FILENAME exists (whether or not you can read it).
2759See also `file-readable-p' and `file-attributes'.
2760This returns nil for a symlink to a nonexistent file.
2761Use `file-symlink-p' to test for such links. */)
2762 (Lisp_Object filename)
2763{
2764 return check_file_access (filename, Qfile_exists_p, F_OK);
2737} 2765}
2738 2766
2739DEFUN ("file-executable-p", Ffile_executable_p, Sfile_executable_p, 1, 1, 0, 2767DEFUN ("file-executable-p", Ffile_executable_p, Sfile_executable_p, 1, 1, 0,
@@ -2743,21 +2771,7 @@ For a directory, this means you can access files in that directory.
2743purpose, though.) */) 2771purpose, though.) */)
2744 (Lisp_Object filename) 2772 (Lisp_Object filename)
2745{ 2773{
2746 Lisp_Object absname; 2774 return check_file_access (filename, Qfile_executable_p, X_OK);
2747 Lisp_Object handler;
2748
2749 CHECK_STRING (filename);
2750 absname = Fexpand_file_name (filename, Qnil);
2751
2752 /* If the file name has special constructs in it,
2753 call the corresponding file name handler. */
2754 handler = Ffind_file_name_handler (absname, Qfile_executable_p);
2755 if (!NILP (handler))
2756 return call2 (handler, Qfile_executable_p, absname);
2757
2758 absname = ENCODE_FILE (absname);
2759
2760 return (check_executable (SSDATA (absname)) ? Qt : Qnil);
2761} 2775}
2762 2776
2763DEFUN ("file-readable-p", Ffile_readable_p, Sfile_readable_p, 1, 1, 0, 2777DEFUN ("file-readable-p", Ffile_readable_p, Sfile_readable_p, 1, 1, 0,
@@ -2765,21 +2779,7 @@ DEFUN ("file-readable-p", Ffile_readable_p, Sfile_readable_p, 1, 1, 0,
2765See also `file-exists-p' and `file-attributes'. */) 2779See also `file-exists-p' and `file-attributes'. */)
2766 (Lisp_Object filename) 2780 (Lisp_Object filename)
2767{ 2781{
2768 Lisp_Object absname; 2782 return check_file_access (filename, Qfile_readable_p, R_OK);
2769 Lisp_Object handler;
2770
2771 CHECK_STRING (filename);
2772 absname = Fexpand_file_name (filename, Qnil);
2773
2774 /* If the file name has special constructs in it,
2775 call the corresponding file name handler. */
2776 handler = Ffind_file_name_handler (absname, Qfile_readable_p);
2777 if (!NILP (handler))
2778 return call2 (handler, Qfile_readable_p, absname);
2779
2780 absname = ENCODE_FILE (absname);
2781 return (faccessat (AT_FDCWD, SSDATA (absname), R_OK, AT_EACCESS) == 0
2782 ? Qt : Qnil);
2783} 2783}
2784 2784
2785DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0, 2785DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
@@ -2789,7 +2789,6 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
2789 Lisp_Object absname, dir, encoded; 2789 Lisp_Object absname, dir, encoded;
2790 Lisp_Object handler; 2790 Lisp_Object handler;
2791 2791
2792 CHECK_STRING (filename);
2793 absname = Fexpand_file_name (filename, Qnil); 2792 absname = Fexpand_file_name (filename, Qnil);
2794 2793
2795 /* If the file name has special constructs in it, 2794 /* If the file name has special constructs in it,
@@ -2799,7 +2798,7 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
2799 return call2 (handler, Qfile_writable_p, absname); 2798 return call2 (handler, Qfile_writable_p, absname);
2800 2799
2801 encoded = ENCODE_FILE (absname); 2800 encoded = ENCODE_FILE (absname);
2802 if (check_writable (SSDATA (encoded), W_OK)) 2801 if (file_access_p (SSDATA (encoded), W_OK))
2803 return Qt; 2802 return Qt;
2804 if (errno != ENOENT) 2803 if (errno != ENOENT)
2805 return Qnil; 2804 return Qnil;
@@ -2810,14 +2809,23 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
2810 dir = Fdirectory_file_name (dir); 2809 dir = Fdirectory_file_name (dir);
2811#endif /* MSDOS */ 2810#endif /* MSDOS */
2812 2811
2813 dir = ENCODE_FILE (dir); 2812 encoded = ENCODE_FILE (dir);
2814#ifdef WINDOWSNT 2813#ifdef WINDOWSNT
2815 /* The read-only attribute of the parent directory doesn't affect 2814 /* The read-only attribute of the parent directory doesn't affect
2816 whether a file or directory can be created within it. Some day we 2815 whether a file or directory can be created within it. Some day we
2817 should check ACLs though, which do affect this. */ 2816 should check ACLs though, which do affect this. */
2818 return file_directory_p (dir) ? Qt : Qnil; 2817 return file_directory_p (encoded) ? Qt : Qnil;
2819#else 2818#else
2820 return check_writable (SSDATA (dir), W_OK | X_OK) ? Qt : Qnil; 2819 if (file_access_p (SSDATA (encoded), W_OK | X_OK))
2820 return Qt;
2821 int err = errno;
2822 if (err == EROFS
2823 || (err == EACCES && file_access_p (SSDATA (encoded), F_OK)))
2824 {
2825 errno = err;
2826 return Qnil;
2827 }
2828 return file_test_errno (absname, err);
2821#endif 2829#endif
2822} 2830}
2823 2831
@@ -2849,8 +2857,8 @@ If there is no error, returns nil. */)
2849} 2857}
2850 2858
2851/* Relative to directory FD, return the symbolic link value of FILENAME. 2859/* Relative to directory FD, return the symbolic link value of FILENAME.
2852 On failure, return nil. */ 2860 On failure, return nil (setting errno). */
2853Lisp_Object 2861static Lisp_Object
2854emacs_readlinkat (int fd, char const *filename) 2862emacs_readlinkat (int fd, char const *filename)
2855{ 2863{
2856 static struct allocator const emacs_norealloc_allocator = 2864 static struct allocator const emacs_norealloc_allocator =
@@ -2869,6 +2877,27 @@ emacs_readlinkat (int fd, char const *filename)
2869 return val; 2877 return val;
2870} 2878}
2871 2879
2880/* Relative to directory FD, return the symbolic link value of FILE.
2881 If FILE is not a symbolic link, return nil (setting errno).
2882 Signal an error if the result cannot be determined. */
2883Lisp_Object
2884check_emacs_readlinkat (int fd, Lisp_Object file, char const *encoded_file)
2885{
2886 Lisp_Object val = emacs_readlinkat (fd, encoded_file);
2887 if (NILP (val))
2888 {
2889 if (errno == EINVAL)
2890 return val;
2891#ifdef CYGWIN
2892 /* Work around Cygwin bugs. */
2893 if (errno == EIO || errno == EACCES)
2894 return val;
2895#endif
2896 return file_metadata_errno ("Reading symbolic link", file, errno);
2897 }
2898 return val;
2899}
2900
2872DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0, 2901DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0,
2873 doc: /* Return non-nil if file FILENAME is the name of a symbolic link. 2902 doc: /* Return non-nil if file FILENAME is the name of a symbolic link.
2874The value is the link target, as a string. 2903The value is the link target, as a string.
@@ -2888,9 +2917,8 @@ This function does not check whether the link target exists. */)
2888 if (!NILP (handler)) 2917 if (!NILP (handler))
2889 return call2 (handler, Qfile_symlink_p, filename); 2918 return call2 (handler, Qfile_symlink_p, filename);
2890 2919
2891 filename = ENCODE_FILE (filename); 2920 return check_emacs_readlinkat (AT_FDCWD, filename,
2892 2921 SSDATA (ENCODE_FILE (filename)));
2893 return emacs_readlinkat (AT_FDCWD, SSDATA (filename));
2894} 2922}
2895 2923
2896DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0, 2924DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0,
@@ -2907,9 +2935,9 @@ See `file-symlink-p' to distinguish symlinks. */)
2907 if (!NILP (handler)) 2935 if (!NILP (handler))
2908 return call2 (handler, Qfile_directory_p, absname); 2936 return call2 (handler, Qfile_directory_p, absname);
2909 2937
2910 absname = ENCODE_FILE (absname); 2938 if (file_directory_p (absname))
2911 2939 return Qt;
2912 return file_directory_p (absname) ? Qt : Qnil; 2940 return file_test_errno (absname, errno);
2913} 2941}
2914 2942
2915/* Return true if FILE is a directory or a symlink to a directory. 2943/* Return true if FILE is a directory or a symlink to a directory.
@@ -2934,7 +2962,7 @@ file_directory_p (Lisp_Object file)
2934 /* O_PATH is defined but evidently this Linux kernel predates 2.6.39. 2962 /* O_PATH is defined but evidently this Linux kernel predates 2.6.39.
2935 Fall back on generic POSIX code. */ 2963 Fall back on generic POSIX code. */
2936# endif 2964# endif
2937 /* Use file_accessible_directory, as it avoids stat EOVERFLOW 2965 /* Use file_accessible_directory_p, as it avoids stat EOVERFLOW
2938 problems and could be cheaper. However, if it fails because FILE 2966 problems and could be cheaper. However, if it fails because FILE
2939 is inaccessible, fall back on stat; if the latter fails with 2967 is inaccessible, fall back on stat; if the latter fails with
2940 EOVERFLOW then FILE must have been a directory unless a race 2968 EOVERFLOW then FILE must have been a directory unless a race
@@ -2990,8 +3018,13 @@ really is a readable and searchable directory. */)
2990 return r; 3018 return r;
2991 } 3019 }
2992 3020
2993 absname = ENCODE_FILE (absname); 3021 Lisp_Object encoded_absname = ENCODE_FILE (absname);
2994 return file_accessible_directory_p (absname) ? Qt : Qnil; 3022 if (file_accessible_directory_p (encoded_absname))
3023 return Qt;
3024 int err = errno;
3025 if (err == EACCES && file_access_p (SSDATA (encoded_absname), F_OK))
3026 return Qnil;
3027 return file_test_errno (absname, err);
2995} 3028}
2996 3029
2997/* If FILE is a searchable directory or a symlink to a 3030/* If FILE is a searchable directory or a symlink to a
@@ -3043,7 +3076,7 @@ file_accessible_directory_p (Lisp_Object file)
3043 dir = buf; 3076 dir = buf;
3044 } 3077 }
3045 3078
3046 ok = check_existing (dir); 3079 ok = file_access_p (dir, F_OK);
3047 saved_errno = errno; 3080 saved_errno = errno;
3048 SAFE_FREE (); 3081 SAFE_FREE ();
3049 errno = saved_errno; 3082 errno = saved_errno;
@@ -3067,27 +3100,21 @@ See `file-symlink-p' to distinguish symlinks. */)
3067 if (!NILP (handler)) 3100 if (!NILP (handler))
3068 return call2 (handler, Qfile_regular_p, absname); 3101 return call2 (handler, Qfile_regular_p, absname);
3069 3102
3070 absname = ENCODE_FILE (absname);
3071
3072#ifdef WINDOWSNT 3103#ifdef WINDOWSNT
3073 { 3104 /* Tell stat to use expensive method to get accurate info. */
3074 int result; 3105 Lisp_Object true_attributes = Vw32_get_true_file_attributes;
3075 Lisp_Object tem = Vw32_get_true_file_attributes; 3106 Vw32_get_true_file_attributes = Qt;
3107#endif
3076 3108
3077 /* Tell stat to use expensive method to get accurate info. */ 3109 int stat_result = stat (SSDATA (absname), &st);
3078 Vw32_get_true_file_attributes = Qt;
3079 result = stat (SSDATA (absname), &st);
3080 Vw32_get_true_file_attributes = tem;
3081 3110
3082 if (result < 0) 3111#ifdef WINDOWSNT
3083 return Qnil; 3112 Vw32_get_true_file_attributes = true_attributes;
3084 return S_ISREG (st.st_mode) ? Qt : Qnil;
3085 }
3086#else
3087 if (stat (SSDATA (absname), &st) < 0)
3088 return Qnil;
3089 return S_ISREG (st.st_mode) ? Qt : Qnil;
3090#endif 3113#endif
3114
3115 if (stat_result == 0)
3116 return S_ISREG (st.st_mode) ? Qt : Qnil;
3117 return file_test_errno (absname, errno);
3091} 3118}
3092 3119
3093DEFUN ("file-selinux-context", Ffile_selinux_context, 3120DEFUN ("file-selinux-context", Ffile_selinux_context,
@@ -3097,7 +3124,7 @@ The return value is a list (USER ROLE TYPE RANGE), where the list
3097elements are strings naming the user, role, type, and range of the 3124elements are strings naming the user, role, type, and range of the
3098file's SELinux security context. 3125file's SELinux security context.
3099 3126
3100Return (nil nil nil nil) if the file is nonexistent or inaccessible, 3127Return (nil nil nil nil) if the file is nonexistent,
3101or if SELinux is disabled, or if Emacs lacks SELinux support. */) 3128or if SELinux is disabled, or if Emacs lacks SELinux support. */)
3102 (Lisp_Object filename) 3129 (Lisp_Object filename)
3103{ 3130{
@@ -3111,13 +3138,11 @@ or if SELinux is disabled, or if Emacs lacks SELinux support. */)
3111 if (!NILP (handler)) 3138 if (!NILP (handler))
3112 return call2 (handler, Qfile_selinux_context, absname); 3139 return call2 (handler, Qfile_selinux_context, absname);
3113 3140
3114 absname = ENCODE_FILE (absname);
3115
3116#if HAVE_LIBSELINUX 3141#if HAVE_LIBSELINUX
3117 if (is_selinux_enabled ()) 3142 if (is_selinux_enabled ())
3118 { 3143 {
3119 security_context_t con; 3144 security_context_t con;
3120 int conlength = lgetfilecon (SSDATA (absname), &con); 3145 int conlength = lgetfilecon (SSDATA (ENCODE_FILE (absname)), &con);
3121 if (conlength > 0) 3146 if (conlength > 0)
3122 { 3147 {
3123 context_t context = context_new (con); 3148 context_t context = context_new (con);
@@ -3132,6 +3157,9 @@ or if SELinux is disabled, or if Emacs lacks SELinux support. */)
3132 context_free (context); 3157 context_free (context);
3133 freecon (con); 3158 freecon (con);
3134 } 3159 }
3160 else if (! (errno == ENOENT || errno == ENOTDIR || errno == ENODATA
3161 || errno == ENOTSUP))
3162 report_file_error ("getting SELinux context", absname);
3135 } 3163 }
3136#endif 3164#endif
3137 3165
@@ -3227,8 +3255,7 @@ DEFUN ("file-acl", Ffile_acl, Sfile_acl, 1, 1, 0,
3227 doc: /* Return ACL entries of file named FILENAME. 3255 doc: /* Return ACL entries of file named FILENAME.
3228The entries are returned in a format suitable for use in `set-file-acl' 3256The entries are returned in a format suitable for use in `set-file-acl'
3229but is otherwise undocumented and subject to change. 3257but is otherwise undocumented and subject to change.
3230Return nil if file does not exist or is not accessible, or if Emacs 3258Return nil if file does not exist. */)
3231was unable to determine the ACL entries. */)
3232 (Lisp_Object filename) 3259 (Lisp_Object filename)
3233{ 3260{
3234 Lisp_Object acl_string = Qnil; 3261 Lisp_Object acl_string = Qnil;
@@ -3243,20 +3270,22 @@ was unable to determine the ACL entries. */)
3243 return call2 (handler, Qfile_acl, absname); 3270 return call2 (handler, Qfile_acl, absname);
3244 3271
3245# ifdef HAVE_ACL_SET_FILE 3272# ifdef HAVE_ACL_SET_FILE
3246 absname = ENCODE_FILE (absname);
3247
3248# ifndef HAVE_ACL_TYPE_EXTENDED 3273# ifndef HAVE_ACL_TYPE_EXTENDED
3249 acl_type_t ACL_TYPE_EXTENDED = ACL_TYPE_ACCESS; 3274 acl_type_t ACL_TYPE_EXTENDED = ACL_TYPE_ACCESS;
3250# endif 3275# endif
3251 acl_t acl = acl_get_file (SSDATA (absname), ACL_TYPE_EXTENDED); 3276 acl_t acl = acl_get_file (SSDATA (ENCODE_FILE (absname)), ACL_TYPE_EXTENDED);
3252 if (acl == NULL) 3277 if (acl == NULL)
3253 return Qnil; 3278 {
3254 3279 if (errno == ENOENT || errno == ENOTDIR || errno == ENOTSUP)
3280 return Qnil;
3281 report_file_error ("Getting ACLs", absname);
3282 }
3255 char *str = acl_to_text (acl, NULL); 3283 char *str = acl_to_text (acl, NULL);
3256 if (str == NULL) 3284 if (str == NULL)
3257 { 3285 {
3286 int err = errno;
3258 acl_free (acl); 3287 acl_free (acl);
3259 return Qnil; 3288 report_file_errno ("Getting ACLs", absname, err);
3260 } 3289 }
3261 3290
3262 acl_string = build_string (str); 3291 acl_string = build_string (str);
@@ -3327,7 +3356,7 @@ support. */)
3327 3356
3328DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0, 3357DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0,
3329 doc: /* Return mode bits of file named FILENAME, as an integer. 3358 doc: /* Return mode bits of file named FILENAME, as an integer.
3330Return nil, if file does not exist or is not accessible. */) 3359Return nil if FILENAME does not exist. */)
3331 (Lisp_Object filename) 3360 (Lisp_Object filename)
3332{ 3361{
3333 struct stat st; 3362 struct stat st;
@@ -3339,11 +3368,8 @@ Return nil, if file does not exist or is not accessible. */)
3339 if (!NILP (handler)) 3368 if (!NILP (handler))
3340 return call2 (handler, Qfile_modes, absname); 3369 return call2 (handler, Qfile_modes, absname);
3341 3370
3342 absname = ENCODE_FILE (absname); 3371 if (stat (SSDATA (ENCODE_FILE (absname)), &st) != 0)
3343 3372 return file_attribute_errno (absname, errno);
3344 if (stat (SSDATA (absname), &st) < 0)
3345 return Qnil;
3346
3347 return make_fixnum (st.st_mode & 07777); 3373 return make_fixnum (st.st_mode & 07777);
3348} 3374}
3349 3375
@@ -3487,14 +3513,27 @@ otherwise, if FILE2 does not exist, the answer is t. */)
3487 if (!NILP (handler)) 3513 if (!NILP (handler))
3488 return call3 (handler, Qfile_newer_than_file_p, absname1, absname2); 3514 return call3 (handler, Qfile_newer_than_file_p, absname1, absname2);
3489 3515
3490 absname1 = ENCODE_FILE (absname1); 3516 int err1;
3491 absname2 = ENCODE_FILE (absname2); 3517 if (stat (SSDATA (ENCODE_FILE (absname1)), &st1) == 0)
3518 err1 = 0;
3519 else
3520 {
3521 err1 = errno;
3522 if (err1 != EOVERFLOW)
3523 return file_test_errno (absname1, err1);
3524 }
3492 3525
3493 if (stat (SSDATA (absname1), &st1) < 0) 3526 if (stat (SSDATA (ENCODE_FILE (absname2)), &st2) != 0)
3494 return Qnil; 3527 {
3528 file_test_errno (absname2, errno);
3529 return Qt;
3530 }
3495 3531
3496 if (stat (SSDATA (absname2), &st2) < 0) 3532 if (err1)
3497 return Qt; 3533 {
3534 file_test_errno (absname1, err1);
3535 eassume (false);
3536 }
3498 3537
3499 return (timespec_cmp (get_stat_mtime (&st2), get_stat_mtime (&st1)) < 0 3538 return (timespec_cmp (get_stat_mtime (&st2), get_stat_mtime (&st1)) < 0
3500 ? Qt : Qnil); 3539 ? Qt : Qnil);
@@ -5686,13 +5725,13 @@ in `current-time' or an integer flag as returned by `visited-file-modtime'. */)
5686 /* The handler can find the file name the same way we did. */ 5725 /* The handler can find the file name the same way we did. */
5687 return call2 (handler, Qset_visited_file_modtime, Qnil); 5726 return call2 (handler, Qset_visited_file_modtime, Qnil);
5688 5727
5689 filename = ENCODE_FILE (filename); 5728 if (stat (SSDATA (ENCODE_FILE (filename)), &st) == 0)
5690
5691 if (stat (SSDATA (filename), &st) >= 0)
5692 { 5729 {
5693 current_buffer->modtime = get_stat_mtime (&st); 5730 current_buffer->modtime = get_stat_mtime (&st);
5694 current_buffer->modtime_size = st.st_size; 5731 current_buffer->modtime_size = st.st_size;
5695 } 5732 }
5733 else
5734 file_attribute_errno (filename, errno);
5696 } 5735 }
5697 5736
5698 return Qnil; 5737 return Qnil;
@@ -6103,22 +6142,22 @@ storage available to a non-superuser. All 3 numbers are in bytes.
6103If the underlying system call fails, value is nil. */) 6142If the underlying system call fails, value is nil. */)
6104 (Lisp_Object filename) 6143 (Lisp_Object filename)
6105{ 6144{
6106 Lisp_Object encoded = ENCODE_FILE (Fexpand_file_name (filename, Qnil)); 6145 filename = Fexpand_file_name (filename, Qnil);
6107 6146
6108 /* If the file name has special constructs in it, 6147 /* If the file name has special constructs in it,
6109 call the corresponding file name handler. */ 6148 call the corresponding file name handler. */
6110 Lisp_Object handler = Ffind_file_name_handler (encoded, Qfile_system_info); 6149 Lisp_Object handler = Ffind_file_name_handler (filename, Qfile_system_info);
6111 if (!NILP (handler)) 6150 if (!NILP (handler))
6112 { 6151 {
6113 Lisp_Object result = call2 (handler, Qfile_system_info, encoded); 6152 Lisp_Object result = call2 (handler, Qfile_system_info, filename);
6114 if (CONSP (result) || NILP (result)) 6153 if (CONSP (result) || NILP (result))
6115 return result; 6154 return result;
6116 error ("Invalid handler in `file-name-handler-alist'"); 6155 error ("Invalid handler in `file-name-handler-alist'");
6117 } 6156 }
6118 6157
6119 struct fs_usage u; 6158 struct fs_usage u;
6120 if (get_fs_usage (SSDATA (encoded), NULL, &u) != 0) 6159 if (get_fs_usage (SSDATA (ENCODE_FILE (filename)), NULL, &u) != 0)
6121 return Qnil; 6160 return errno == ENOSYS ? Qnil : file_attribute_errno (filename, errno);
6122 return list3 (blocks_to_bytes (u.fsu_blocksize, u.fsu_blocks, false), 6161 return list3 (blocks_to_bytes (u.fsu_blocksize, u.fsu_blocks, false),
6123 blocks_to_bytes (u.fsu_blocksize, u.fsu_bfree, false), 6162 blocks_to_bytes (u.fsu_blocksize, u.fsu_bfree, false),
6124 blocks_to_bytes (u.fsu_blocksize, u.fsu_bavail, 6163 blocks_to_bytes (u.fsu_blocksize, u.fsu_bavail,
diff --git a/src/filelock.c b/src/filelock.c
index 46349a63e4a..ff25d6475de 100644
--- a/src/filelock.c
+++ b/src/filelock.c
@@ -504,9 +504,9 @@ read_lock_data (char *lfname, char lfinfo[MAX_LFINFO + 1])
504} 504}
505 505
506/* Return 0 if nobody owns the lock file LFNAME or the lock is obsolete, 506/* Return 0 if nobody owns the lock file LFNAME or the lock is obsolete,
507 1 if another process owns it (and set OWNER (if non-null) to info), 507 -1 if another process owns it (and set OWNER (if non-null) to info),
508 2 if the current process owns it, 508 -2 if the current process owns it,
509 or -1 if something is wrong with the locking mechanism. */ 509 or an errno value if something is wrong with the locking mechanism. */
510 510
511static int 511static int
512current_lock_owner (lock_info_type *owner, char *lfname) 512current_lock_owner (lock_info_type *owner, char *lfname)
@@ -525,23 +525,23 @@ current_lock_owner (lock_info_type *owner, char *lfname)
525 /* If nonexistent lock file, all is well; otherwise, got strange error. */ 525 /* If nonexistent lock file, all is well; otherwise, got strange error. */
526 lfinfolen = read_lock_data (lfname, owner->user); 526 lfinfolen = read_lock_data (lfname, owner->user);
527 if (lfinfolen < 0) 527 if (lfinfolen < 0)
528 return errno == ENOENT ? 0 : -1; 528 return errno == ENOENT ? 0 : errno;
529 if (MAX_LFINFO < lfinfolen) 529 if (MAX_LFINFO < lfinfolen)
530 return -1; 530 return ENAMETOOLONG;
531 owner->user[lfinfolen] = 0; 531 owner->user[lfinfolen] = 0;
532 532
533 /* Parse USER@HOST.PID:BOOT_TIME. If can't parse, return -1. */ 533 /* Parse USER@HOST.PID:BOOT_TIME. If can't parse, return EINVAL. */
534 /* The USER is everything before the last @. */ 534 /* The USER is everything before the last @. */
535 owner->at = at = memrchr (owner->user, '@', lfinfolen); 535 owner->at = at = memrchr (owner->user, '@', lfinfolen);
536 if (!at) 536 if (!at)
537 return -1; 537 return EINVAL;
538 owner->dot = dot = strrchr (at, '.'); 538 owner->dot = dot = strrchr (at, '.');
539 if (!dot) 539 if (!dot)
540 return -1; 540 return EINVAL;
541 541
542 /* The PID is everything from the last '.' to the ':' or equivalent. */ 542 /* The PID is everything from the last '.' to the ':' or equivalent. */
543 if (! c_isdigit (dot[1])) 543 if (! c_isdigit (dot[1]))
544 return -1; 544 return EINVAL;
545 errno = 0; 545 errno = 0;
546 pid = strtoimax (dot + 1, &owner->colon, 10); 546 pid = strtoimax (dot + 1, &owner->colon, 10);
547 if (errno == ERANGE) 547 if (errno == ERANGE)
@@ -562,20 +562,20 @@ current_lock_owner (lock_info_type *owner, char *lfname)
562 mistakenly transliterate ':' to U+F022 in symlink contents. 562 mistakenly transliterate ':' to U+F022 in symlink contents.
563 See <https://bugzilla.redhat.com/show_bug.cgi?id=1384153>. */ 563 See <https://bugzilla.redhat.com/show_bug.cgi?id=1384153>. */
564 if (! (boot[0] == '\200' && boot[1] == '\242')) 564 if (! (boot[0] == '\200' && boot[1] == '\242'))
565 return -1; 565 return EINVAL;
566 boot += 2; 566 boot += 2;
567 FALLTHROUGH; 567 FALLTHROUGH;
568 case ':': 568 case ':':
569 if (! c_isdigit (boot[0])) 569 if (! c_isdigit (boot[0]))
570 return -1; 570 return EINVAL;
571 boot_time = strtoimax (boot, &lfinfo_end, 10); 571 boot_time = strtoimax (boot, &lfinfo_end, 10);
572 break; 572 break;
573 573
574 default: 574 default:
575 return -1; 575 return EINVAL;
576 } 576 }
577 if (lfinfo_end != owner->user + lfinfolen) 577 if (lfinfo_end != owner->user + lfinfolen)
578 return -1; 578 return EINVAL;
579 579
580 /* On current host? */ 580 /* On current host? */
581 Lisp_Object system_name = Fsystem_name (); 581 Lisp_Object system_name = Fsystem_name ();
@@ -584,22 +584,22 @@ current_lock_owner (lock_info_type *owner, char *lfname)
584 && memcmp (at + 1, SSDATA (system_name), SBYTES (system_name)) == 0) 584 && memcmp (at + 1, SSDATA (system_name), SBYTES (system_name)) == 0)
585 { 585 {
586 if (pid == getpid ()) 586 if (pid == getpid ())
587 ret = 2; /* We own it. */ 587 ret = -2; /* We own it. */
588 else if (0 < pid && pid <= TYPE_MAXIMUM (pid_t) 588 else if (0 < pid && pid <= TYPE_MAXIMUM (pid_t)
589 && (kill (pid, 0) >= 0 || errno == EPERM) 589 && (kill (pid, 0) >= 0 || errno == EPERM)
590 && (boot_time == 0 590 && (boot_time == 0
591 || (boot_time <= TYPE_MAXIMUM (time_t) 591 || (boot_time <= TYPE_MAXIMUM (time_t)
592 && within_one_second (boot_time, get_boot_time ())))) 592 && within_one_second (boot_time, get_boot_time ()))))
593 ret = 1; /* An existing process on this machine owns it. */ 593 ret = -1; /* An existing process on this machine owns it. */
594 /* The owner process is dead or has a strange pid, so try to 594 /* The owner process is dead or has a strange pid, so try to
595 zap the lockfile. */ 595 zap the lockfile. */
596 else 596 else
597 return unlink (lfname); 597 return unlink (lfname) < 0 ? errno : 0;
598 } 598 }
599 else 599 else
600 { /* If we wanted to support the check for stale locks on remote machines, 600 { /* If we wanted to support the check for stale locks on remote machines,
601 here's where we'd do it. */ 601 here's where we'd do it. */
602 ret = 1; 602 ret = -1;
603 } 603 }
604 604
605 return ret; 605 return ret;
@@ -608,9 +608,9 @@ current_lock_owner (lock_info_type *owner, char *lfname)
608 608
609/* Lock the lock named LFNAME if possible. 609/* Lock the lock named LFNAME if possible.
610 Return 0 in that case. 610 Return 0 in that case.
611 Return positive if some other process owns the lock, and info about 611 Return negative if some other process owns the lock, and info about
612 that process in CLASHER. 612 that process in CLASHER.
613 Return -1 if cannot lock for any other reason. */ 613 Return positive errno value if cannot lock for any other reason. */
614 614
615static int 615static int
616lock_if_free (lock_info_type *clasher, char *lfname) 616lock_if_free (lock_info_type *clasher, char *lfname)
@@ -618,20 +618,18 @@ lock_if_free (lock_info_type *clasher, char *lfname)
618 int err; 618 int err;
619 while ((err = lock_file_1 (lfname, 0)) == EEXIST) 619 while ((err = lock_file_1 (lfname, 0)) == EEXIST)
620 { 620 {
621 switch (current_lock_owner (clasher, lfname)) 621 err = current_lock_owner (clasher, lfname);
622 if (err != 0)
622 { 623 {
623 case 2: 624 if (err < 0)
624 return 0; /* We ourselves locked it. */ 625 return -2 - err; /* We locked it, or someone else has it. */
625 case 1: 626 break; /* current_lock_owner returned strange error. */
626 return 1; /* Someone else has it. */
627 case -1:
628 return -1; /* current_lock_owner returned strange error. */
629 } 627 }
630 628
631 /* We deleted a stale lock; try again to lock the file. */ 629 /* We deleted a stale lock; try again to lock the file. */
632 } 630 }
633 631
634 return err ? -1 : 0; 632 return err;
635} 633}
636 634
637/* lock_file locks file FN, 635/* lock_file locks file FN,
@@ -697,8 +695,9 @@ lock_file (Lisp_Object fn)
697 /* Create the name of the lock-file for file fn */ 695 /* Create the name of the lock-file for file fn */
698 MAKE_LOCK_NAME (lfname, encoded_fn); 696 MAKE_LOCK_NAME (lfname, encoded_fn);
699 697
700 /* Try to lock the lock. */ 698 /* Try to lock the lock. FIXME: This ignores errors when
701 if (0 < lock_if_free (&lock_info, lfname)) 699 lock_if_free returns a positive errno value. */
700 if (lock_if_free (&lock_info, lfname) < 0)
702 { 701 {
703 /* Someone else has the lock. Consider breaking it. */ 702 /* Someone else has the lock. Consider breaking it. */
704 Lisp_Object attack; 703 Lisp_Object attack;
@@ -725,13 +724,16 @@ unlock_file (Lisp_Object fn)
725 char *lfname; 724 char *lfname;
726 USE_SAFE_ALLOCA; 725 USE_SAFE_ALLOCA;
727 726
728 fn = Fexpand_file_name (fn, Qnil); 727 Lisp_Object filename = Fexpand_file_name (fn, Qnil);
729 fn = ENCODE_FILE (fn); 728 fn = ENCODE_FILE (filename);
730 729
731 MAKE_LOCK_NAME (lfname, fn); 730 MAKE_LOCK_NAME (lfname, fn);
732 731
733 if (current_lock_owner (0, lfname) == 2) 732 int err = current_lock_owner (0, lfname);
734 unlink (lfname); 733 if (err == -2 && unlink (lfname) != 0 && errno != ENOENT)
734 err = errno;
735 if (0 < err)
736 report_file_errno ("Unlocking file", filename, err);
735 737
736 SAFE_FREE (); 738 SAFE_FREE ();
737} 739}
@@ -822,17 +824,17 @@ t if it is locked by you, else a string saying which user has locked it. */)
822 USE_SAFE_ALLOCA; 824 USE_SAFE_ALLOCA;
823 825
824 filename = Fexpand_file_name (filename, Qnil); 826 filename = Fexpand_file_name (filename, Qnil);
825 filename = ENCODE_FILE (filename); 827 Lisp_Object encoded_filename = ENCODE_FILE (filename);
826 828 MAKE_LOCK_NAME (lfname, encoded_filename);
827 MAKE_LOCK_NAME (lfname, filename);
828 829
829 owner = current_lock_owner (&locker, lfname); 830 owner = current_lock_owner (&locker, lfname);
830 if (owner <= 0) 831 switch (owner)
831 ret = Qnil; 832 {
832 else if (owner == 2) 833 case -2: ret = Qt; break;
833 ret = Qt; 834 case -1: ret = make_string (locker.user, locker.at - locker.user); break;
834 else 835 case 0: ret = Qnil; break;
835 ret = make_string (locker.user, locker.at - locker.user); 836 default: report_file_errno ("Testing file lock", filename, owner);
837 }
836 838
837 SAFE_FREE (); 839 SAFE_FREE ();
838 return ret; 840 return ret;
diff --git a/src/lisp.h b/src/lisp.h
index 02f8a7b6686..e68d2732e21 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4299,7 +4299,6 @@ extern void syms_of_marker (void);
4299 4299
4300/* Defined in fileio.c. */ 4300/* Defined in fileio.c. */
4301 4301
4302extern bool check_executable (char *);
4303extern char *splice_dir_file (char *, char const *, char const *); 4302extern char *splice_dir_file (char *, char const *, char const *);
4304extern bool file_name_absolute_p (const char *); 4303extern bool file_name_absolute_p (const char *);
4305extern char const *get_homedir (void); 4304extern char const *get_homedir (void);
@@ -4310,12 +4309,14 @@ extern Lisp_Object write_region (Lisp_Object, Lisp_Object, Lisp_Object,
4310extern void close_file_unwind (int); 4309extern void close_file_unwind (int);
4311extern void fclose_unwind (void *); 4310extern void fclose_unwind (void *);
4312extern void restore_point_unwind (Lisp_Object); 4311extern void restore_point_unwind (Lisp_Object);
4312extern bool file_access_p (char const *, int);
4313extern Lisp_Object get_file_errno_data (const char *, Lisp_Object, int); 4313extern Lisp_Object get_file_errno_data (const char *, Lisp_Object, int);
4314extern AVOID report_file_errno (const char *, Lisp_Object, int); 4314extern AVOID report_file_errno (const char *, Lisp_Object, int);
4315extern AVOID report_file_error (const char *, Lisp_Object); 4315extern AVOID report_file_error (const char *, Lisp_Object);
4316extern AVOID report_file_notify_error (const char *, Lisp_Object); 4316extern AVOID report_file_notify_error (const char *, Lisp_Object);
4317extern Lisp_Object file_attribute_errno (Lisp_Object, int);
4317extern bool internal_delete_file (Lisp_Object); 4318extern bool internal_delete_file (Lisp_Object);
4318extern Lisp_Object emacs_readlinkat (int, const char *); 4319extern Lisp_Object check_emacs_readlinkat (int, Lisp_Object, char const *);
4319extern bool file_directory_p (Lisp_Object); 4320extern bool file_directory_p (Lisp_Object);
4320extern bool file_accessible_directory_p (Lisp_Object); 4321extern bool file_accessible_directory_p (Lisp_Object);
4321extern void init_fileio (void); 4322extern void init_fileio (void);
diff --git a/src/lread.c b/src/lread.c
index 6ae7a0d8ba0..d8883db46c1 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1346,15 +1346,22 @@ Return t if the file exists and loads successfully. */)
1346 if (!load_prefer_newer && is_elc) 1346 if (!load_prefer_newer && is_elc)
1347 { 1347 {
1348 result = stat (SSDATA (efound), &s1); 1348 result = stat (SSDATA (efound), &s1);
1349 int err = errno;
1349 if (result == 0) 1350 if (result == 0)
1350 { 1351 {
1351 SSET (efound, SBYTES (efound) - 1, 0); 1352 SSET (efound, SBYTES (efound) - 1, 0);
1352 result = stat (SSDATA (efound), &s2); 1353 result = stat (SSDATA (efound), &s2);
1354 err = errno;
1353 SSET (efound, SBYTES (efound) - 1, 'c'); 1355 SSET (efound, SBYTES (efound) - 1, 'c');
1356 if (result != 0)
1357 found = Fsubstring (found, make_fixnum (0),
1358 make_fixnum (-1));
1354 } 1359 }
1355 1360 if (result != 0)
1356 if (result == 0 1361 file_attribute_errno (found, err);
1357 && timespec_cmp (get_stat_mtime (&s1), get_stat_mtime (&s2)) < 0) 1362 else if (timespec_cmp (get_stat_mtime (&s1),
1363 get_stat_mtime (&s2))
1364 < 0)
1358 { 1365 {
1359 /* Make the progress messages mention that source is newer. */ 1366 /* Make the progress messages mention that source is newer. */
1360 newer = 1; 1367 newer = 1;
@@ -1748,16 +1755,20 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
1748 { 1755 {
1749 if (file_directory_p (encoded_fn)) 1756 if (file_directory_p (encoded_fn))
1750 last_errno = EISDIR; 1757 last_errno = EISDIR;
1751 else 1758 else if (errno == ENOENT || errno == ENOTDIR)
1752 fd = 1; 1759 fd = 1;
1760 else
1761 last_errno = errno;
1753 } 1762 }
1763 else if (! (errno == ENOENT || errno == ENOTDIR))
1764 last_errno = errno;
1754 } 1765 }
1755 else 1766 else
1756 { 1767 {
1757 fd = emacs_open (pfn, O_RDONLY, 0); 1768 fd = emacs_open (pfn, O_RDONLY, 0);
1758 if (fd < 0) 1769 if (fd < 0)
1759 { 1770 {
1760 if (errno != ENOENT) 1771 if (! (errno == ENOENT || errno == ENOTDIR))
1761 last_errno = errno; 1772 last_errno = errno;
1762 } 1773 }
1763 else 1774 else