diff options
Diffstat (limited to 'src/fileio.c')
-rw-r--r-- | src/fileio.c | 478 |
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, |