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.
This commit is contained in:
Paul Eggert 2019-09-17 19:18:14 -07:00
parent ae3edf0ac3
commit 9dc306b1db
8 changed files with 282 additions and 223 deletions

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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';

View file

@ -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;
if (stat (file, &st) != 0)
return false;
errno = EPERM;
return st.st_mode & S_IWRITE || S_ISDIR (st.st_mode);
}
#endif /* CYGWIN */
return res;
#endif /* not MSDOS */
#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,6 +2720,40 @@ file_name_absolute_p (char const *filename)
|| user_homedir (&filename[1]))));
}
/* 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. */
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 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 ok;
}
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);
}
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'.
@ -2715,25 +2761,7 @@ 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;
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);
if (!NILP (handler))
{
Lisp_Object result = call2 (handler, Qfile_exists_p, absname);
errno = 0;
return result;
}
absname = ENCODE_FILE (absname);
return check_existing (SSDATA (absname)) ? Qt : Qnil;
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
/* 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
int stat_result = stat (SSDATA (absname), &st);
#ifdef WINDOWSNT
{
int result;
Lisp_Object tem = Vw32_get_true_file_attributes;
/* 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;
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;
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,

View file

@ -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 USER@HOST.PID:BOOT_TIME. If can't parse, return -1. */
/* Parse USER@HOST.PID: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;

View file

@ -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);

View file

@ -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