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:
parent
ae3edf0ac3
commit
9dc306b1db
8 changed files with 282 additions and 223 deletions
|
@ -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
|
||||
|
||||
|
|
10
etc/NEWS
10
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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
359
src/fileio.c
359
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;
|
||||
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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
21
src/lread.c
21
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
|
||||
|
|
Loading…
Add table
Reference in a new issue