summaryrefslogtreecommitdiff
path: root/src/fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fileio.c')
-rw-r--r--src/fileio.c478
1 files changed, 275 insertions, 203 deletions
diff --git a/src/fileio.c b/src/fileio.c
index cbc0c89cf3e..5337ea5c800 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -134,60 +134,45 @@ static dev_t timestamp_file_system;
is added here. */
static Lisp_Object Vwrite_region_annotation_buffers;
+static Lisp_Object file_name_directory (Lisp_Object);
static bool a_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
Lisp_Object *, struct coding_system *);
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,
@@ -250,6 +235,44 @@ 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);
+}
+
+/* In theory, EACCES errors for predicates like file-readable-p should
+ be checked further because they may be problems with an ancestor
+ directory instead of with the file itself, which means that we
+ don't have reliable info about the requested file. In practice,
+ though, such errors are common enough that signaling them can be
+ annoying even if the errors are real (e.g., Bug#37445). So return
+ nil for EACCES unless compiling with -DPICKY_EACCES, which is off
+ by default. */
+#ifndef PICKY_EACCES
+enum { PICKY_EACCES = false };
+#endif
+
+Lisp_Object
+file_test_errno (Lisp_Object file, int err)
+{
+ if (!PICKY_EACCES && err == EACCES)
+ return Qnil;
+ return file_metadata_errno ("Testing file", file, err);
+}
+
void
close_file_unwind (int fd)
{
@@ -356,6 +379,15 @@ Given a Unix syntax file name, returns a string ending in slash. */)
return STRINGP (handled_name) ? handled_name : Qnil;
}
+ return file_name_directory (filename);
+}
+
+/* Return the directory component of FILENAME, or nil if FILENAME does
+ not contain a directory component. */
+
+static Lisp_Object
+file_name_directory (Lisp_Object filename)
+{
char *beg = SSDATA (filename);
char const *p = beg + SBYTES (filename);
@@ -2369,41 +2401,48 @@ internal_delete_file (Lisp_Object filename)
return NILP (tem);
}
-/* Filesystems are case-sensitive on all supported systems except
- MS-Windows, MS-DOS, Cygwin, and Mac OS X. They are always
- case-insensitive on the first two, but they may or may not be
- case-insensitive on Cygwin and OS X. The following function
- attempts to provide a runtime test on those two systems. If the
- test is not conclusive, we assume case-insensitivity on Cygwin and
- case-sensitivity on Mac OS X.
-
- FIXME: Mounted filesystems on Posix hosts, like Samba shares or
- NFS-mounted Windows volumes, might be case-insensitive. Can we
- detect this? */
+/* Return -1 if FILE is a case-insensitive file name, 0 if not,
+ and a positive errno value if the result cannot be determined. */
-static bool
-file_name_case_insensitive_p (const char *filename)
+static int
+file_name_case_insensitive_err (Lisp_Object file)
{
- /* Use pathconf with _PC_CASE_INSENSITIVE or _PC_CASE_SENSITIVE if
- those flags are available. As of this writing (2017-05-20),
+ /* Filesystems are case-sensitive on all supported systems except
+ MS-Windows, MS-DOS, Cygwin, and macOS. They are always
+ case-insensitive on the first two, but they may or may not be
+ case-insensitive on Cygwin and macOS so do a runtime test on
+ those two systems. If the test is not conclusive, assume
+ case-insensitivity on Cygwin and case-sensitivity on macOS.
+
+ FIXME: Mounted filesystems on Posix hosts, like Samba shares or
+ NFS-mounted Windows volumes, might be case-insensitive. Can we
+ detect this?
+
+ Use pathconf with _PC_CASE_INSENSITIVE or _PC_CASE_SENSITIVE if
+ those flags are available. As of this writing (2019-09-15),
Cygwin is the only platform known to support the former (starting
with Cygwin-2.6.1), and macOS is the only platform known to
support the latter. */
-#ifdef _PC_CASE_INSENSITIVE
- int res = pathconf (filename, _PC_CASE_INSENSITIVE);
+#if defined _PC_CASE_INSENSITIVE || defined _PC_CASE_SENSITIVE
+ char *filename = SSDATA (ENCODE_FILE (file));
+# ifdef _PC_CASE_INSENSITIVE
+ long int res = pathconf (filename, _PC_CASE_INSENSITIVE);
if (res >= 0)
- return res > 0;
-#elif defined _PC_CASE_SENSITIVE
- int res = pathconf (filename, _PC_CASE_SENSITIVE);
+ return - (res > 0);
+# else
+ long int res = pathconf (filename, _PC_CASE_SENSITIVE);
if (res >= 0)
- return res == 0;
+ return - (res == 0);
+# endif
+ if (errno != EINVAL)
+ return errno;
#endif
#if defined CYGWIN || defined DOS_NT
- return true;
+ return -1;
#else
- return false;
+ return 0;
#endif
}
@@ -2426,21 +2465,22 @@ The arg must be a string. */)
/* If the file doesn't exist, move up the filesystem tree until we
reach an existing directory or the root. */
- if (NILP (Ffile_exists_p (filename)))
+ while (true)
{
- filename = Ffile_name_directory (filename);
- while (NILP (Ffile_exists_p (filename)))
+ int err = file_name_case_insensitive_err (filename);
+ switch (err)
{
- Lisp_Object newname = expand_and_dir_to_file (filename);
- /* Avoid infinite loop if the root is reported as non-existing
- (impossible?). */
- if (!NILP (Fstring_equal (newname, filename)))
- break;
- filename = newname;
+ 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?). */
+ if (!NILP (Fstring_equal (parent, filename)))
+ return Qnil;
+ filename = parent;
}
- filename = ENCODE_FILE (filename);
- return file_name_case_insensitive_p (SSDATA (filename)) ? Qt : Qnil;
}
DEFUN ("rename-file", Frename_file, Srename_file, 2, 3,
@@ -2546,7 +2586,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);
@@ -2694,32 +2734,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
+ || (PICKY_EACCES && 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,
@@ -2729,21 +2785,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,
@@ -2751,21 +2793,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,
@@ -2775,7 +2803,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,
@@ -2785,25 +2812,34 @@ 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;
- dir = Ffile_name_directory (absname);
+ dir = file_name_directory (absname);
eassert (!NILP (dir));
#ifdef MSDOS
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
}
@@ -2835,8 +2871,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 =
@@ -2855,6 +2891,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.
@@ -2874,9 +2931,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,
@@ -2893,9 +2949,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.
@@ -2905,7 +2961,10 @@ file_directory_p (Lisp_Object file)
{
#ifdef DOS_NT
/* This is cheaper than 'stat'. */
- return faccessat (AT_FDCWD, SSDATA (file), D_OK, AT_EACCESS) == 0;
+ bool retval = faccessat (AT_FDCWD, SSDATA (file), D_OK, AT_EACCESS) == 0;
+ if (!retval && errno == EACCES)
+ errno = ENOTDIR; /* like the non-DOS_NT branch below does */
+ return retval;
#else
# ifdef O_PATH
/* Use O_PATH if available, as it avoids races and EOVERFLOW issues. */
@@ -2920,7 +2979,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
@@ -2976,8 +3035,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
@@ -3029,7 +3093,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;
@@ -3053,27 +3117,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,
@@ -3083,7 +3141,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)
{
@@ -3097,13 +3155,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);
@@ -3118,6 +3174,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
@@ -3213,8 +3272,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;
@@ -3229,20 +3287,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);
@@ -3313,7 +3373,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;
@@ -3325,11 +3385,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);
}
@@ -3473,14 +3530,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);
@@ -3612,7 +3682,7 @@ file_offset (Lisp_Object val)
static struct timespec
time_error_value (int errnum)
{
- int ns = (errnum == ENOENT || errnum == EACCES || errnum == ENOTDIR
+ int ns = (errnum == ENOENT || errnum == ENOTDIR
? NONEXISTENT_MODTIME_NSECS
: UNKNOWN_MODTIME_NSECS);
return make_timespec (0, ns);
@@ -5672,13 +5742,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;
@@ -5822,7 +5892,7 @@ A non-nil CURRENT-ONLY argument means save only current buffer. */)
if (!NILP (Vrun_hooks))
{
Lisp_Object dir;
- dir = Ffile_name_directory (listfile);
+ dir = file_name_directory (listfile);
if (NILP (Ffile_directory_p (dir)))
internal_condition_case_1 (do_auto_save_make_dir,
dir, Qt,
@@ -6067,16 +6137,18 @@ effect except for flushing STREAM's data. */)
#ifndef DOS_NT
-/* Yield a Lisp float as close as possible to BLOCKSIZE * BLOCKS, with
- the result negated if NEGATE. */
+/* Yield a Lisp number equal to BLOCKSIZE * BLOCKS, with the result
+ negated if NEGATE. */
static Lisp_Object
blocks_to_bytes (uintmax_t blocksize, uintmax_t blocks, bool negate)
{
- /* On typical platforms the following code is accurate to 53 bits,
- which is close enough. BLOCKSIZE is invariably a power of 2, so
- converting it to double does not lose information. */
- double bs = blocksize;
- return make_float (negate ? -bs * -blocks : bs * blocks);
+ intmax_t n;
+ if (!INT_MULTIPLY_WRAPV (blocksize, blocks, &n))
+ return make_int (negate ? -n : n);
+ Lisp_Object bs = make_uint (blocksize);
+ if (negate)
+ bs = CALLN (Fminus, bs);
+ return CALLN (Ftimes, bs, make_uint (blocks));
}
DEFUN ("file-system-info", Ffile_system_info, Sfile_system_info, 1, 1, 0,
@@ -6087,22 +6159,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,