emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 9dc306b1: Improve reporting of I/O, access errors


From: Paul Eggert
Subject: [Emacs-diffs] master 9dc306b1: Improve reporting of I/O, access errors
Date: Tue, 17 Sep 2019 22:24:43 -0400 (EDT)

branch: master
commit 9dc306b1db08196684d05a474148e16305adbad0
Author: Paul Eggert <address@hidden>
Commit: Paul Eggert <address@hidden>

    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.
---
 doc/lispref/files.texi |  13 +-
 etc/NEWS               |  10 ++
 src/dired.c            |   9 +-
 src/emacs.c            |   2 +-
 src/fileio.c           | 359 +++++++++++++++++++++++++++----------------------
 src/filelock.c         |  86 ++++++------
 src/lisp.h             |   5 +-
 src/lread.c            |  21 ++-
 8 files changed, 282 insertions(+), 223 deletions(-)

diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index 18a1f49..fba9622 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -856,8 +856,7 @@ systems, this is true if the file exists and you have 
execute
 permission on the containing directories, regardless of the
 permissions of the file itself.)
 
-If the file does not exist, or if access control policies prevent you
-from finding its attributes, this function returns @code{nil}.
+If the file does not exist, this function returns @code{nil}.
 
 Directories are files, so @code{file-exists-p} can return @code{t} when
 given a directory.  However, because @code{file-exists-p} follows
@@ -1262,7 +1261,7 @@ on the 19th, @file{aug-20} was written on the 20th, and 
the file
 @defun file-attributes filename &optional id-format
 @anchor{Definition of file-attributes}
 This function returns a list of attributes of file @var{filename}.  If
-the specified file's attributes cannot be accessed, it returns @code{nil}.
+the specified file does not exist, it returns @code{nil}.
 This function does not follow symbolic links.
 The optional parameter @var{id-format} specifies the preferred format
 of attributes @acronym{UID} and @acronym{GID} (see below)---the
@@ -1464,9 +1463,8 @@ The underlying ACL implementation is platform-specific; 
on GNU/Linux
 and BSD, Emacs uses the POSIX ACL interface, while on MS-Windows Emacs
 emulates the POSIX ACL interface with native file security APIs.
 
-If Emacs was not compiled with ACL support, or the file does not exist
-or is inaccessible, or Emacs was unable to determine the ACL entries
-for any other reason, then the return value is @code{nil}.
+If ACLs are not supported or the file does not exist,
+then the return value is @code{nil}.
 @end defun
 
 @defun file-selinux-context filename
@@ -1478,8 +1476,7 @@ for details about what these actually mean.  The return 
value has the
 same form as what @code{set-file-selinux-context} takes for its
 @var{context} argument (@pxref{Changing Files}).
 
-If Emacs was not compiled with SELinux support, or the file does not
-exist or is inaccessible, or if the system does not support SELinux,
+If SELinux is not supported or the file does not exist,
 then the return value is @code{(nil nil nil nil)}.
 @end defun
 
diff --git a/etc/NEWS b/etc/NEWS
index 9aec8da..dce4903 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2005,6 +2005,16 @@ file name if there is no user named "foo".
 ** The FILENAME argument to 'file-name-base' is now mandatory and no
 longer defaults to 'buffer-file-name'.
 
++++
+** File metadata primitives now signal an error if I/O, access, or
+other serious errors prevent them from determining the result.
+Formerly, these functions often (though not always) returned nil.
+For example, if the directory /etc/firewalld is not searchable,
+(file-symlink-p "/etc/firewalld/firewalld.conf") now signals an error
+instead of returning nil, because file-symlink-p cannot determine
+whether a symbolic link exists there.  These functions still behave as
+before if the only problem is that the file does not exist.
+
 ---
 ** The function 'eldoc-message' now accepts a single argument.
 Programs that called it with multiple arguments before should pass
diff --git a/src/dired.c b/src/dired.c
index df03bc3..3768b6d 100644
--- a/src/dired.c
+++ b/src/dired.c
@@ -819,7 +819,7 @@ stat_gname (struct stat *st)
 
 DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 2, 0,
        doc: /* Return a list of attributes of file FILENAME.
-Value is nil if specified file cannot be opened.
+Value is nil if specified file does not exist.
 
 ID-FORMAT specifies the preferred format of attributes uid and gid (see
 below) - valid values are `string' and `integer'.  The latter is the
@@ -939,15 +939,14 @@ file_attributes (int fd, char const *name,
         information to be accurate.  */
       w32_stat_get_owner_group = 1;
 #endif
-      if (fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW) == 0)
-       err = 0;
+      err = fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW) == 0 ? 0 : errno;
 #ifdef WINDOWSNT
       w32_stat_get_owner_group = 0;
 #endif
     }
 
   if (err != 0)
-    return unbind_to (count, Qnil);
+    return unbind_to (count, file_attribute_errno (filename, err));
 
   Lisp_Object file_type;
   if (S_ISLNK (s.st_mode))
@@ -956,7 +955,7 @@ file_attributes (int fd, char const *name,
         symlink is replaced between the call to fstatat and the call
         to emacs_readlinkat.  Detect this race unless the replacement
         is also a symlink.  */
-      file_type = emacs_readlinkat (fd, name);
+      file_type = check_emacs_readlinkat (fd, filename, name);
       if (NILP (file_type))
        return unbind_to (count, Qnil);
     }
diff --git a/src/emacs.c b/src/emacs.c
index 558dd11..eb73281 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -746,7 +746,7 @@ load_pdump_find_executable (char const *argv0, ptrdiff_t 
*candidate_size)
       candidate[path_part_length] = DIRECTORY_SEP;
       memcpy (candidate + path_part_length + 1, argv0, argv0_length + 1);
       struct stat st;
-      if (check_executable (candidate)
+      if (file_access_p (candidate, X_OK)
          && stat (candidate, &st) == 0 && S_ISREG (st.st_mode))
        return candidate;
       *candidate = '\0';
diff --git a/src/fileio.c b/src/fileio.c
index 81c29ca..0977516 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -141,54 +141,38 @@ static bool e_write (int, Lisp_Object, ptrdiff_t, 
ptrdiff_t,
                     struct coding_system *);
 
 
-/* Return true if FILENAME exists, otherwise return false and set errno.  */
-
-static bool
-check_existing (const char *filename)
-{
-  return faccessat (AT_FDCWD, filename, F_OK, AT_EACCESS) == 0;
-}
-
-/* Return true if file FILENAME exists and can be executed.  */
+/* Test whether FILE is accessible for AMODE.
+   Return true if successful, false (setting errno) otherwise.  */
 
 bool
-check_executable (char *filename)
-{
-  return faccessat (AT_FDCWD, filename, X_OK, AT_EACCESS) == 0;
-}
-
-/* Return true if file FILENAME exists and can be accessed
-   according to AMODE, which should include W_OK.
-   On failure, return false and set errno.  */
-
-static bool
-check_writable (const char *filename, int amode)
+file_access_p (char const *file, int amode)
 {
 #ifdef MSDOS
-  /* FIXME: an faccessat implementation should be added to the
-     DOS/Windows ports and this #ifdef branch should be removed.  */
-  struct stat st;
-  if (stat (filename, &st) < 0)
-    return 0;
-  errno = EPERM;
-  return (st.st_mode & S_IWRITE || S_ISDIR (st.st_mode));
-#else /* not MSDOS */
-  bool res = faccessat (AT_FDCWD, filename, amode, AT_EACCESS) == 0;
-#ifdef CYGWIN
-  /* faccessat may have returned failure because Cygwin couldn't
-     determine the file's UID or GID; if so, we return success.  */
-  if (!res)
+  if (amode & W_OK)
     {
-      int faccessat_errno = errno;
+      /* FIXME: The MS-DOS faccessat implementation should handle this.  */
       struct stat st;
-      if (stat (filename, &st) < 0)
-        return 0;
-      res = (st.st_uid == -1 || st.st_gid == -1);
-      errno = faccessat_errno;
-    }
-#endif /* CYGWIN */
-  return res;
-#endif /* not MSDOS */
+      if (stat (file, &st) != 0)
+       return false;
+      errno = EPERM;
+      return st.st_mode & S_IWRITE || S_ISDIR (st.st_mode);
+    }
+#endif
+
+  if (faccessat (AT_FDCWD, file, amode, AT_EACCESS) == 0)
+    return true;
+
+#ifdef CYGWIN
+  /* Return success if faccessat failed because Cygwin couldn't
+     determine the file's UID or GID.  */
+  int err = errno;
+  struct stat st;
+  if (stat (file, &st) == 0 && (st.st_uid == -1 || st.st_gid == -1))
+    return true;
+  errno = err;
+#endif
+
+  return false;
 }
 
 /* Signal a file-access failure.  STRING describes the failure,
@@ -251,6 +235,30 @@ report_file_notify_error (const char *string, Lisp_Object 
name)
 }
 #endif
 
+/* ACTION failed for FILE with errno ERR.  Signal an error if ERR
+   means the file's metadata could not be retrieved even though it may
+   exist, otherwise return nil.  */
+
+static Lisp_Object
+file_metadata_errno (char const *action, Lisp_Object file, int err)
+{
+  if (err == ENOENT || err == ENOTDIR || err == 0)
+    return Qnil;
+  report_file_errno (action, file, err);
+}
+
+Lisp_Object
+file_attribute_errno (Lisp_Object file, int err)
+{
+  return file_metadata_errno ("Getting attributes", file, err);
+}
+
+static Lisp_Object
+file_test_errno (Lisp_Object file, int err)
+{
+  return file_metadata_errno ("Testing file", file, err);
+}
+
 void
 close_file_unwind (int fd)
 {
@@ -2446,8 +2454,12 @@ The arg must be a string.  */)
   while (true)
     {
       int err = file_name_case_insensitive_err (filename);
-      if (! (err == ENOENT || err == ENOTDIR))
-       return err < 0 ? Qt : Qnil;
+      switch (err)
+       {
+       case -1: return Qt;
+       default: return file_test_errno (filename, err);
+       case ENOENT: case ENOTDIR: break;
+       }
       Lisp_Object parent = file_name_directory (filename);
       /* Avoid infinite loop if the root is reported as non-existing
         (impossible?).  */
@@ -2560,7 +2572,7 @@ This is what happens in interactive use with M-x.  */)
     {
       Lisp_Object symlink_target
        = (S_ISLNK (file_st.st_mode)
-          ? emacs_readlinkat (AT_FDCWD, SSDATA (encoded_file))
+          ? check_emacs_readlinkat (AT_FDCWD, file, SSDATA (encoded_file))
           : Qnil);
       if (!NILP (symlink_target))
        Fmake_symbolic_link (symlink_target, newname, ok_if_already_exists);
@@ -2708,32 +2720,48 @@ file_name_absolute_p (char const *filename)
                  || user_homedir (&filename[1]))));
 }
 
-DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0,
-       doc: /* Return t if file FILENAME exists (whether or not you can read 
it).
-See also `file-readable-p' and `file-attributes'.
-This returns nil for a symlink to a nonexistent file.
-Use `file-symlink-p' to test for such links.  */)
-  (Lisp_Object filename)
-{
-  Lisp_Object absname;
-  Lisp_Object handler;
+/* Return t if FILE exists and is accessible via OPERATION and AMODE,
+   nil (setting errno) if not.  Signal an error if the result cannot
+   be determined.  */
 
-  CHECK_STRING (filename);
-  absname = Fexpand_file_name (filename, Qnil);
-
-  /* If the file name has special constructs in it,
-     call the corresponding file name handler.  */
-  handler = Ffind_file_name_handler (absname, Qfile_exists_p);
+static Lisp_Object
+check_file_access (Lisp_Object file, Lisp_Object operation, int amode)
+{
+  file = Fexpand_file_name (file, Qnil);
+  Lisp_Object handler = Ffind_file_name_handler (file, operation);
   if (!NILP (handler))
     {
-      Lisp_Object result = call2 (handler, Qfile_exists_p, absname);
+      Lisp_Object ok = call2 (handler, operation, file);
+      /* This errno value is bogus.  Any caller that depends on errno
+        should be rethought anyway, to avoid a race between testing a
+        handled file's accessibility and using the file.  */
       errno = 0;
-      return result;
+      return ok;
     }
 
-  absname = ENCODE_FILE (absname);
+  char *encoded_file = SSDATA (ENCODE_FILE (file));
+  bool ok = file_access_p (encoded_file, amode);
+  if (ok)
+    return Qt;
+  int err = errno;
+  if (err == EROFS || err == ETXTBSY
+      || (err == EACCES && amode != F_OK
+         && file_access_p (encoded_file, F_OK)))
+    {
+      errno = err;
+      return Qnil;
+    }
+  return file_test_errno (file, err);
+}
 
-  return check_existing (SSDATA (absname)) ? Qt : Qnil;
+DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0,
+       doc: /* Return t if file FILENAME exists (whether or not you can read 
it).
+See also `file-readable-p' and `file-attributes'.
+This returns nil for a symlink to a nonexistent file.
+Use `file-symlink-p' to test for such links.  */)
+  (Lisp_Object filename)
+{
+  return check_file_access (filename, Qfile_exists_p, F_OK);
 }
 
 DEFUN ("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.
 purpose, though.)  */)
   (Lisp_Object filename)
 {
-  Lisp_Object absname;
-  Lisp_Object handler;
-
-  CHECK_STRING (filename);
-  absname = Fexpand_file_name (filename, Qnil);
-
-  /* If the file name has special constructs in it,
-     call the corresponding file name handler.  */
-  handler = Ffind_file_name_handler (absname, Qfile_executable_p);
-  if (!NILP (handler))
-    return call2 (handler, Qfile_executable_p, absname);
-
-  absname = ENCODE_FILE (absname);
-
-  return (check_executable (SSDATA (absname)) ? Qt : Qnil);
+  return check_file_access (filename, Qfile_executable_p, X_OK);
 }
 
 DEFUN ("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,
 See also `file-exists-p' and `file-attributes'.  */)
   (Lisp_Object filename)
 {
-  Lisp_Object absname;
-  Lisp_Object handler;
-
-  CHECK_STRING (filename);
-  absname = Fexpand_file_name (filename, Qnil);
-
-  /* If the file name has special constructs in it,
-     call the corresponding file name handler.  */
-  handler = Ffind_file_name_handler (absname, Qfile_readable_p);
-  if (!NILP (handler))
-    return call2 (handler, Qfile_readable_p, absname);
-
-  absname = ENCODE_FILE (absname);
-  return (faccessat (AT_FDCWD, SSDATA (absname), R_OK, AT_EACCESS) == 0
-         ? Qt : Qnil);
+  return check_file_access (filename, Qfile_readable_p, R_OK);
 }
 
 DEFUN ("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,
   Lisp_Object absname, dir, encoded;
   Lisp_Object handler;
 
-  CHECK_STRING (filename);
   absname = Fexpand_file_name (filename, Qnil);
 
   /* 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,
     return call2 (handler, Qfile_writable_p, absname);
 
   encoded = ENCODE_FILE (absname);
-  if (check_writable (SSDATA (encoded), W_OK))
+  if (file_access_p (SSDATA (encoded), W_OK))
     return Qt;
   if (errno != ENOENT)
     return Qnil;
@@ -2810,14 +2809,23 @@ DEFUN ("file-writable-p", Ffile_writable_p, 
Sfile_writable_p, 1, 1, 0,
   dir = Fdirectory_file_name (dir);
 #endif /* MSDOS */
 
-  dir = ENCODE_FILE (dir);
+  encoded = ENCODE_FILE (dir);
 #ifdef WINDOWSNT
   /* The read-only attribute of the parent directory doesn't affect
      whether a file or directory can be created within it.  Some day we
      should check ACLs though, which do affect this.  */
-  return file_directory_p (dir) ? Qt : Qnil;
+  return file_directory_p (encoded) ? Qt : Qnil;
 #else
-  return check_writable (SSDATA (dir), W_OK | X_OK) ? Qt : Qnil;
+  if (file_access_p (SSDATA (encoded), W_OK | X_OK))
+    return Qt;
+  int err = errno;
+  if (err == EROFS
+      || (err == EACCES && file_access_p (SSDATA (encoded), F_OK)))
+    {
+      errno = err;
+      return Qnil;
+    }
+  return file_test_errno (absname, err);
 #endif
 }
 
@@ -2849,8 +2857,8 @@ If there is no error, returns nil.  */)
 }
 
 /* Relative to directory FD, return the symbolic link value of FILENAME.
-   On failure, return nil.  */
-Lisp_Object
+   On failure, return nil (setting errno).  */
+static Lisp_Object
 emacs_readlinkat (int fd, char const *filename)
 {
   static struct allocator const emacs_norealloc_allocator =
@@ -2869,6 +2877,27 @@ emacs_readlinkat (int fd, char const *filename)
   return val;
 }
 
+/* Relative to directory FD, return the symbolic link value of FILE.
+   If FILE is not a symbolic link, return nil (setting errno).
+   Signal an error if the result cannot be determined.  */
+Lisp_Object
+check_emacs_readlinkat (int fd, Lisp_Object file, char const *encoded_file)
+{
+  Lisp_Object val = emacs_readlinkat (fd, encoded_file);
+  if (NILP (val))
+    {
+      if (errno == EINVAL)
+       return val;
+#ifdef CYGWIN
+      /* Work around Cygwin bugs.  */
+      if (errno == EIO || errno == EACCES)
+       return val;
+#endif
+      return file_metadata_errno ("Reading symbolic link", file, errno);
+    }
+  return val;
+}
+
 DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0,
        doc: /* Return non-nil if file FILENAME is the name of a symbolic link.
 The value is the link target, as a string.
@@ -2888,9 +2917,8 @@ This function does not check whether the link target 
exists.  */)
   if (!NILP (handler))
     return call2 (handler, Qfile_symlink_p, filename);
 
-  filename = ENCODE_FILE (filename);
-
-  return emacs_readlinkat (AT_FDCWD, SSDATA (filename));
+  return check_emacs_readlinkat (AT_FDCWD, filename,
+                                SSDATA (ENCODE_FILE (filename)));
 }
 
 DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0,
@@ -2907,9 +2935,9 @@ See `file-symlink-p' to distinguish symlinks.  */)
   if (!NILP (handler))
     return call2 (handler, Qfile_directory_p, absname);
 
-  absname = ENCODE_FILE (absname);
-
-  return file_directory_p (absname) ? Qt : Qnil;
+  if (file_directory_p (absname))
+    return Qt;
+  return file_test_errno (absname, errno);
 }
 
 /* Return true if FILE is a directory or a symlink to a directory.
@@ -2934,7 +2962,7 @@ file_directory_p (Lisp_Object file)
   /* O_PATH is defined but evidently this Linux kernel predates 2.6.39.
      Fall back on generic POSIX code.  */
 # endif
-  /* Use file_accessible_directory, as it avoids stat EOVERFLOW
+  /* Use file_accessible_directory_p, as it avoids stat EOVERFLOW
      problems and could be cheaper.  However, if it fails because FILE
      is inaccessible, fall back on stat; if the latter fails with
      EOVERFLOW then FILE must have been a directory unless a race
@@ -2990,8 +3018,13 @@ really is a readable and searchable directory.  */)
       return r;
     }
 
-  absname = ENCODE_FILE (absname);
-  return file_accessible_directory_p (absname) ? Qt : Qnil;
+  Lisp_Object encoded_absname = ENCODE_FILE (absname);
+  if (file_accessible_directory_p (encoded_absname))
+    return Qt;
+  int err = errno;
+  if (err == EACCES && file_access_p (SSDATA (encoded_absname), F_OK))
+    return Qnil;
+  return file_test_errno (absname, err);
 }
 
 /* If FILE is a searchable directory or a symlink to a
@@ -3043,7 +3076,7 @@ file_accessible_directory_p (Lisp_Object file)
       dir = buf;
     }
 
-  ok = check_existing (dir);
+  ok = file_access_p (dir, F_OK);
   saved_errno = errno;
   SAFE_FREE ();
   errno = saved_errno;
@@ -3067,27 +3100,21 @@ See `file-symlink-p' to distinguish symlinks.  */)
   if (!NILP (handler))
     return call2 (handler, Qfile_regular_p, absname);
 
-  absname = ENCODE_FILE (absname);
-
 #ifdef WINDOWSNT
-  {
-    int result;
-    Lisp_Object tem = Vw32_get_true_file_attributes;
+  /* Tell stat to use expensive method to get accurate info.  */
+  Lisp_Object true_attributes = Vw32_get_true_file_attributes;
+  Vw32_get_true_file_attributes = Qt;
+#endif
 
-    /* Tell stat to use expensive method to get accurate info.  */
-    Vw32_get_true_file_attributes = Qt;
-    result = stat (SSDATA (absname), &st);
-    Vw32_get_true_file_attributes = tem;
+  int stat_result = stat (SSDATA (absname), &st);
 
-    if (result < 0)
-      return Qnil;
-    return S_ISREG (st.st_mode) ? Qt : Qnil;
-  }
-#else
-  if (stat (SSDATA (absname), &st) < 0)
-    return Qnil;
-  return S_ISREG (st.st_mode) ? Qt : Qnil;
+#ifdef WINDOWSNT
+  Vw32_get_true_file_attributes = true_attributes;
 #endif
+
+  if (stat_result == 0)
+    return S_ISREG (st.st_mode) ? Qt : Qnil;
+  return file_test_errno (absname, errno);
 }
 
 DEFUN ("file-selinux-context", Ffile_selinux_context,
@@ -3097,7 +3124,7 @@ The return value is a list (USER ROLE TYPE RANGE), where 
the list
 elements are strings naming the user, role, type, and range of the
 file's SELinux security context.
 
-Return (nil nil nil nil) if the file is nonexistent or inaccessible,
+Return (nil nil nil nil) if the file is nonexistent,
 or if SELinux is disabled, or if Emacs lacks SELinux support.  */)
   (Lisp_Object filename)
 {
@@ -3111,13 +3138,11 @@ or if SELinux is disabled, or if Emacs lacks SELinux 
support.  */)
   if (!NILP (handler))
     return call2 (handler, Qfile_selinux_context, absname);
 
-  absname = ENCODE_FILE (absname);
-
 #if HAVE_LIBSELINUX
   if (is_selinux_enabled ())
     {
       security_context_t con;
-      int conlength = lgetfilecon (SSDATA (absname), &con);
+      int conlength = lgetfilecon (SSDATA (ENCODE_FILE (absname)), &con);
       if (conlength > 0)
        {
          context_t context = context_new (con);
@@ -3132,6 +3157,9 @@ or if SELinux is disabled, or if Emacs lacks SELinux 
support.  */)
          context_free (context);
          freecon (con);
        }
+      else if (! (errno == ENOENT || errno == ENOTDIR || errno == ENODATA
+                 || errno == ENOTSUP))
+       report_file_error ("getting SELinux context", absname);
     }
 #endif
 
@@ -3227,8 +3255,7 @@ DEFUN ("file-acl", Ffile_acl, Sfile_acl, 1, 1, 0,
        doc: /* Return ACL entries of file named FILENAME.
 The entries are returned in a format suitable for use in `set-file-acl'
 but is otherwise undocumented and subject to change.
-Return nil if file does not exist or is not accessible, or if Emacs
-was unable to determine the ACL entries.  */)
+Return nil if file does not exist.  */)
   (Lisp_Object filename)
 {
   Lisp_Object acl_string = Qnil;
@@ -3243,20 +3270,22 @@ was unable to determine the ACL entries.  */)
     return call2 (handler, Qfile_acl, absname);
 
 # ifdef HAVE_ACL_SET_FILE
-  absname = ENCODE_FILE (absname);
-
 #  ifndef HAVE_ACL_TYPE_EXTENDED
   acl_type_t ACL_TYPE_EXTENDED = ACL_TYPE_ACCESS;
 #  endif
-  acl_t acl = acl_get_file (SSDATA (absname), ACL_TYPE_EXTENDED);
+  acl_t acl = acl_get_file (SSDATA (ENCODE_FILE (absname)), ACL_TYPE_EXTENDED);
   if (acl == NULL)
-    return Qnil;
-
+    {
+      if (errno == ENOENT || errno == ENOTDIR || errno == ENOTSUP)
+       return Qnil;
+      report_file_error ("Getting ACLs", absname);
+    }
   char *str = acl_to_text (acl, NULL);
   if (str == NULL)
     {
+      int err = errno;
       acl_free (acl);
-      return Qnil;
+      report_file_errno ("Getting ACLs", absname, err);
     }
 
   acl_string = build_string (str);
@@ -3327,7 +3356,7 @@ support.  */)
 
 DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0,
        doc: /* Return mode bits of file named FILENAME, as an integer.
-Return nil, if file does not exist or is not accessible.  */)
+Return nil if FILENAME does not exist.  */)
   (Lisp_Object filename)
 {
   struct stat st;
@@ -3339,11 +3368,8 @@ Return nil, if file does not exist or is not accessible. 
 */)
   if (!NILP (handler))
     return call2 (handler, Qfile_modes, absname);
 
-  absname = ENCODE_FILE (absname);
-
-  if (stat (SSDATA (absname), &st) < 0)
-    return Qnil;
-
+  if (stat (SSDATA (ENCODE_FILE (absname)), &st) != 0)
+    return file_attribute_errno (absname, errno);
   return make_fixnum (st.st_mode & 07777);
 }
 
@@ -3487,14 +3513,27 @@ otherwise, if FILE2 does not exist, the answer is t.  
*/)
   if (!NILP (handler))
     return call3 (handler, Qfile_newer_than_file_p, absname1, absname2);
 
-  absname1 = ENCODE_FILE (absname1);
-  absname2 = ENCODE_FILE (absname2);
+  int err1;
+  if (stat (SSDATA (ENCODE_FILE (absname1)), &st1) == 0)
+    err1 = 0;
+  else
+    {
+      err1 = errno;
+      if (err1 != EOVERFLOW)
+       return file_test_errno (absname1, err1);
+    }
 
-  if (stat (SSDATA (absname1), &st1) < 0)
-    return Qnil;
+  if (stat (SSDATA (ENCODE_FILE (absname2)), &st2) != 0)
+    {
+      file_test_errno (absname2, errno);
+      return Qt;
+    }
 
-  if (stat (SSDATA (absname2), &st2) < 0)
-    return Qt;
+  if (err1)
+    {
+      file_test_errno (absname1, err1);
+      eassume (false);
+    }
 
   return (timespec_cmp (get_stat_mtime (&st2), get_stat_mtime (&st1)) < 0
          ? Qt : Qnil);
@@ -5686,13 +5725,13 @@ in `current-time' or an integer flag as returned by 
`visited-file-modtime'.  */)
        /* The handler can find the file name the same way we did.  */
        return call2 (handler, Qset_visited_file_modtime, Qnil);
 
-      filename = ENCODE_FILE (filename);
-
-      if (stat (SSDATA (filename), &st) >= 0)
+      if (stat (SSDATA (ENCODE_FILE (filename)), &st) == 0)
         {
          current_buffer->modtime = get_stat_mtime (&st);
           current_buffer->modtime_size = st.st_size;
         }
+      else
+       file_attribute_errno (filename, errno);
     }
 
   return Qnil;
@@ -6103,22 +6142,22 @@ storage available to a non-superuser.  All 3 numbers 
are in bytes.
 If the underlying system call fails, value is nil.  */)
   (Lisp_Object filename)
 {
-  Lisp_Object encoded = ENCODE_FILE (Fexpand_file_name (filename, Qnil));
+  filename = Fexpand_file_name (filename, Qnil);
 
   /* If the file name has special constructs in it,
      call the corresponding file name handler.  */
-  Lisp_Object handler = Ffind_file_name_handler (encoded, Qfile_system_info);
+  Lisp_Object handler = Ffind_file_name_handler (filename, Qfile_system_info);
   if (!NILP (handler))
     {
-      Lisp_Object result = call2 (handler, Qfile_system_info, encoded);
+      Lisp_Object result = call2 (handler, Qfile_system_info, filename);
       if (CONSP (result) || NILP (result))
        return result;
       error ("Invalid handler in `file-name-handler-alist'");
     }
 
   struct fs_usage u;
-  if (get_fs_usage (SSDATA (encoded), NULL, &u) != 0)
-    return Qnil;
+  if (get_fs_usage (SSDATA (ENCODE_FILE (filename)), NULL, &u) != 0)
+    return errno == ENOSYS ? Qnil : file_attribute_errno (filename, errno);
   return list3 (blocks_to_bytes (u.fsu_blocksize, u.fsu_blocks, false),
                blocks_to_bytes (u.fsu_blocksize, u.fsu_bfree, false),
                blocks_to_bytes (u.fsu_blocksize, u.fsu_bavail,
diff --git a/src/filelock.c b/src/filelock.c
index 46349a6..ff25d64 100644
--- a/src/filelock.c
+++ b/src/filelock.c
@@ -504,9 +504,9 @@ read_lock_data (char *lfname, char lfinfo[MAX_LFINFO + 1])
 }
 
 /* Return 0 if nobody owns the lock file LFNAME or the lock is obsolete,
-   1 if another process owns it (and set OWNER (if non-null) to info),
-   2 if the current process owns it,
-   or -1 if something is wrong with the locking mechanism.  */
+   -1 if another process owns it (and set OWNER (if non-null) to info),
+   -2 if the current process owns it,
+   or an errno value if something is wrong with the locking mechanism.  */
 
 static int
 current_lock_owner (lock_info_type *owner, char *lfname)
@@ -525,23 +525,23 @@ current_lock_owner (lock_info_type *owner, char *lfname)
   /* If nonexistent lock file, all is well; otherwise, got strange error. */
   lfinfolen = read_lock_data (lfname, owner->user);
   if (lfinfolen < 0)
-    return errno == ENOENT ? 0 : -1;
+    return errno == ENOENT ? 0 : errno;
   if (MAX_LFINFO < lfinfolen)
-    return -1;
+    return ENAMETOOLONG;
   owner->user[lfinfolen] = 0;
 
-  /* Parse address@hidden:BOOT_TIME.  If can't parse, return -1.  */
+  /* Parse address@hidden:BOOT_TIME.  If can't parse, return EINVAL.  */
   /* The USER is everything before the last @.  */
   owner->at = at = memrchr (owner->user, '@', lfinfolen);
   if (!at)
-    return -1;
+    return EINVAL;
   owner->dot = dot = strrchr (at, '.');
   if (!dot)
-    return -1;
+    return EINVAL;
 
   /* The PID is everything from the last '.' to the ':' or equivalent.  */
   if (! c_isdigit (dot[1]))
-    return -1;
+    return EINVAL;
   errno = 0;
   pid = strtoimax (dot + 1, &owner->colon, 10);
   if (errno == ERANGE)
@@ -562,20 +562,20 @@ current_lock_owner (lock_info_type *owner, char *lfname)
         mistakenly transliterate ':' to U+F022 in symlink contents.
         See <https://bugzilla.redhat.com/show_bug.cgi?id=1384153>.  */
       if (! (boot[0] == '\200' && boot[1] == '\242'))
-       return -1;
+       return EINVAL;
       boot += 2;
       FALLTHROUGH;
     case ':':
       if (! c_isdigit (boot[0]))
-       return -1;
+       return EINVAL;
       boot_time = strtoimax (boot, &lfinfo_end, 10);
       break;
 
     default:
-      return -1;
+      return EINVAL;
     }
   if (lfinfo_end != owner->user + lfinfolen)
-    return -1;
+    return EINVAL;
 
   /* On current host?  */
   Lisp_Object system_name = Fsystem_name ();
@@ -584,22 +584,22 @@ current_lock_owner (lock_info_type *owner, char *lfname)
       && memcmp (at + 1, SSDATA (system_name), SBYTES (system_name)) == 0)
     {
       if (pid == getpid ())
-        ret = 2; /* We own it.  */
+        ret = -2; /* We own it.  */
       else if (0 < pid && pid <= TYPE_MAXIMUM (pid_t)
                && (kill (pid, 0) >= 0 || errno == EPERM)
               && (boot_time == 0
                   || (boot_time <= TYPE_MAXIMUM (time_t)
                       && within_one_second (boot_time, get_boot_time ()))))
-        ret = 1; /* An existing process on this machine owns it.  */
+        ret = -1; /* An existing process on this machine owns it.  */
       /* The owner process is dead or has a strange pid, so try to
          zap the lockfile.  */
       else
-        return unlink (lfname);
+        return unlink (lfname) < 0 ? errno : 0;
     }
   else
     { /* If we wanted to support the check for stale locks on remote machines,
          here's where we'd do it.  */
-      ret = 1;
+      ret = -1;
     }
 
   return ret;
@@ -608,9 +608,9 @@ current_lock_owner (lock_info_type *owner, char *lfname)
 
 /* Lock the lock named LFNAME if possible.
    Return 0 in that case.
-   Return positive if some other process owns the lock, and info about
+   Return negative if some other process owns the lock, and info about
      that process in CLASHER.
-   Return -1 if cannot lock for any other reason.  */
+   Return positive errno value if cannot lock for any other reason.  */
 
 static int
 lock_if_free (lock_info_type *clasher, char *lfname)
@@ -618,20 +618,18 @@ lock_if_free (lock_info_type *clasher, char *lfname)
   int err;
   while ((err = lock_file_1 (lfname, 0)) == EEXIST)
     {
-      switch (current_lock_owner (clasher, lfname))
+      err = current_lock_owner (clasher, lfname);
+      if (err != 0)
        {
-       case 2:
-         return 0;   /* We ourselves locked it.  */
-       case 1:
-         return 1;   /* Someone else has it.  */
-       case -1:
-         return -1;  /* current_lock_owner returned strange error.  */
+         if (err < 0)
+           return -2 - err; /* We locked it, or someone else has it.  */
+         break; /* current_lock_owner returned strange error.  */
        }
 
       /* We deleted a stale lock; try again to lock the file.  */
     }
 
-  return err ? -1 : 0;
+  return err;
 }
 
 /* lock_file locks file FN,
@@ -697,8 +695,9 @@ lock_file (Lisp_Object fn)
       /* Create the name of the lock-file for file fn */
       MAKE_LOCK_NAME (lfname, encoded_fn);
 
-      /* Try to lock the lock.  */
-      if (0 < lock_if_free (&lock_info, lfname))
+      /* Try to lock the lock.  FIXME: This ignores errors when
+        lock_if_free returns a positive errno value.  */
+      if (lock_if_free (&lock_info, lfname) < 0)
        {
          /* Someone else has the lock.  Consider breaking it.  */
          Lisp_Object attack;
@@ -725,13 +724,16 @@ unlock_file (Lisp_Object fn)
   char *lfname;
   USE_SAFE_ALLOCA;
 
-  fn = Fexpand_file_name (fn, Qnil);
-  fn = ENCODE_FILE (fn);
+  Lisp_Object filename = Fexpand_file_name (fn, Qnil);
+  fn = ENCODE_FILE (filename);
 
   MAKE_LOCK_NAME (lfname, fn);
 
-  if (current_lock_owner (0, lfname) == 2)
-    unlink (lfname);
+  int err = current_lock_owner (0, lfname);
+  if (err == -2 && unlink (lfname) != 0 && errno != ENOENT)
+    err = errno;
+  if (0 < err)
+    report_file_errno ("Unlocking file", filename, err);
 
   SAFE_FREE ();
 }
@@ -822,17 +824,17 @@ t if it is locked by you, else a string saying which user 
has locked it.  */)
   USE_SAFE_ALLOCA;
 
   filename = Fexpand_file_name (filename, Qnil);
-  filename = ENCODE_FILE (filename);
-
-  MAKE_LOCK_NAME (lfname, filename);
+  Lisp_Object encoded_filename = ENCODE_FILE (filename);
+  MAKE_LOCK_NAME (lfname, encoded_filename);
 
   owner = current_lock_owner (&locker, lfname);
-  if (owner <= 0)
-    ret = Qnil;
-  else if (owner == 2)
-    ret = Qt;
-  else
-    ret = make_string (locker.user, locker.at - locker.user);
+  switch (owner)
+    {
+    case -2: ret = Qt; break;
+    case -1: ret = make_string (locker.user, locker.at - locker.user); break;
+    case  0: ret = Qnil; break;
+    default: report_file_errno ("Testing file lock", filename, owner);
+    }
 
   SAFE_FREE ();
   return ret;
diff --git a/src/lisp.h b/src/lisp.h
index 02f8a7b..e68d273 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4299,7 +4299,6 @@ extern void syms_of_marker (void);
 
 /* Defined in fileio.c.  */
 
-extern bool check_executable (char *);
 extern char *splice_dir_file (char *, char const *, char const *);
 extern bool file_name_absolute_p (const char *);
 extern char const *get_homedir (void);
@@ -4310,12 +4309,14 @@ extern Lisp_Object write_region (Lisp_Object, 
Lisp_Object, Lisp_Object,
 extern void close_file_unwind (int);
 extern void fclose_unwind (void *);
 extern void restore_point_unwind (Lisp_Object);
+extern bool file_access_p (char const *, int);
 extern Lisp_Object get_file_errno_data (const char *, Lisp_Object, int);
 extern AVOID report_file_errno (const char *, Lisp_Object, int);
 extern AVOID report_file_error (const char *, Lisp_Object);
 extern AVOID report_file_notify_error (const char *, Lisp_Object);
+extern Lisp_Object file_attribute_errno (Lisp_Object, int);
 extern bool internal_delete_file (Lisp_Object);
-extern Lisp_Object emacs_readlinkat (int, const char *);
+extern Lisp_Object check_emacs_readlinkat (int, Lisp_Object, char const *);
 extern bool file_directory_p (Lisp_Object);
 extern bool file_accessible_directory_p (Lisp_Object);
 extern void init_fileio (void);
diff --git a/src/lread.c b/src/lread.c
index 6ae7a0d..d8883db 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1346,15 +1346,22 @@ Return t if the file exists and loads successfully.  */)
           if (!load_prefer_newer && is_elc)
             {
               result = stat (SSDATA (efound), &s1);
+             int err = errno;
               if (result == 0)
                 {
                   SSET (efound, SBYTES (efound) - 1, 0);
                   result = stat (SSDATA (efound), &s2);
+                 err = errno;
                   SSET (efound, SBYTES (efound) - 1, 'c');
+                 if (result != 0)
+                   found = Fsubstring (found, make_fixnum (0),
+                                       make_fixnum (-1));
                 }
-
-              if (result == 0
-                  && timespec_cmp (get_stat_mtime (&s1), get_stat_mtime (&s2)) 
< 0)
+             if (result != 0)
+               file_attribute_errno (found, err);
+             else if (timespec_cmp (get_stat_mtime (&s1),
+                                    get_stat_mtime (&s2))
+                      < 0)
                 {
                   /* Make the progress messages mention that source is newer.  
*/
                   newer = 1;
@@ -1748,16 +1755,20 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object 
suffixes,
                  {
                    if (file_directory_p (encoded_fn))
                      last_errno = EISDIR;
-                   else
+                   else if (errno == ENOENT || errno == ENOTDIR)
                      fd = 1;
+                   else
+                     last_errno = errno;
                  }
+               else if (! (errno == ENOENT || errno == ENOTDIR))
+                 last_errno = errno;
              }
            else
              {
                fd = emacs_open (pfn, O_RDONLY, 0);
                if (fd < 0)
                  {
-                   if (errno != ENOENT)
+                   if (! (errno == ENOENT || errno == ENOTDIR))
                      last_errno = errno;
                  }
                else



reply via email to

[Prev in Thread] Current Thread [Next in Thread]