summaryrefslogtreecommitdiff
path: root/src/fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fileio.c')
-rw-r--r--src/fileio.c113
1 files changed, 98 insertions, 15 deletions
diff --git a/src/fileio.c b/src/fileio.c
index c4a10000bc3..52ca8b6297e 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -96,6 +96,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <acl.h>
#include <allocator.h>
#include <careadlinkat.h>
+#include <fsusage.h>
#include <stat-time.h>
#include <tempname.h>
@@ -138,7 +139,7 @@ static bool e_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
struct coding_system *);
-/* Return true if FILENAME exists. */
+/* Return true if FILENAME exists, otherwise return false and set errno. */
static bool
check_existing (const char *filename)
@@ -2594,7 +2595,7 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
/* 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 (SSDATA (dir)) ? Qt : Qnil;
+ return file_directory_p (dir) ? Qt : Qnil;
#else
return check_writable (SSDATA (dir), W_OK | X_OK) ? Qt : Qnil;
#endif
@@ -2688,19 +2689,47 @@ See `file-symlink-p' to distinguish symlinks. */)
absname = ENCODE_FILE (absname);
- return file_directory_p (SSDATA (absname)) ? Qt : Qnil;
+ return file_directory_p (absname) ? Qt : Qnil;
}
-/* Return true if FILE is a directory or a symlink to a directory. */
+/* Return true if FILE is a directory or a symlink to a directory.
+ Otherwise return false and set errno. */
bool
-file_directory_p (char const *file)
+file_directory_p (Lisp_Object file)
{
-#ifdef WINDOWSNT
+#ifdef DOS_NT
/* This is cheaper than 'stat'. */
- return faccessat (AT_FDCWD, file, D_OK, AT_EACCESS) == 0;
+ return faccessat (AT_FDCWD, SSDATA (file), D_OK, AT_EACCESS) == 0;
#else
+# ifdef O_PATH
+ /* Use O_PATH if available, as it avoids races and EOVERFLOW issues. */
+ int fd = openat (AT_FDCWD, SSDATA (file), O_PATH | O_CLOEXEC | O_DIRECTORY);
+ if (0 <= fd)
+ {
+ emacs_close (fd);
+ return true;
+ }
+ if (errno != EINVAL)
+ return false;
+ /* 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
+ 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
+ condition occurred (a problem hard to work around portably). */
+ if (file_accessible_directory_p (file))
+ return true;
+ if (errno != EACCES)
+ return false;
struct stat st;
- return stat (file, &st) == 0 && S_ISDIR (st.st_mode);
+ if (stat (SSDATA (file), &st) != 0)
+ return errno == EOVERFLOW;
+ if (S_ISDIR (st.st_mode))
+ return true;
+ errno = ENOTDIR;
+ return false;
#endif
}
@@ -2761,7 +2790,7 @@ file_accessible_directory_p (Lisp_Object file)
return (SBYTES (file) == 0
|| w32_accessible_directory_p (SSDATA (file), SBYTES (file)));
# else /* MSDOS */
- return file_directory_p (SSDATA (file));
+ return file_directory_p (file);
# endif /* MSDOS */
#else /* !DOS_NT */
/* On POSIXish platforms, use just one system call; this avoids a
@@ -2782,12 +2811,15 @@ file_accessible_directory_p (Lisp_Object file)
dir = data;
else
{
- /* Just check for trailing '/' when deciding whether to append '/'.
- That's simpler than testing the two special cases "/" and "//",
- and it's a safe optimization here. */
- char *buf = SAFE_ALLOCA (len + 3);
+ /* Just check for trailing '/' when deciding whether append '/'
+ before appending '.'. That's simpler than testing the two
+ special cases "/" and "//", and it's a safe optimization
+ here. After appending '.', append another '/' to work around
+ a macOS bug (Bug#30350). */
+ static char const appended[] = "/./";
+ char *buf = SAFE_ALLOCA (len + sizeof appended);
memcpy (buf, data, len);
- strcpy (buf + len, &"/."[data[len - 1] == '/']);
+ strcpy (buf + len, &appended[data[len - 1] == '/']);
dir = buf;
}
@@ -3191,7 +3223,7 @@ Use the current time if TIMESTAMP is nil. TIMESTAMP is in the format of
{
#ifdef MSDOS
/* Setting times on a directory always fails. */
- if (file_directory_p (SSDATA (encoded_absname)))
+ if (file_directory_p (encoded_absname))
return Qnil;
#endif
report_file_error ("Setting file times", absname);
@@ -5786,6 +5818,52 @@ effect except for flushing STREAM's data. */)
return (set_binary_mode (fileno (fp), binmode) == O_BINARY) ? Qt : Qnil;
}
+#ifndef DOS_NT
+
+/* Yield a Lisp float as close as possible 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);
+}
+
+DEFUN ("file-system-info", Ffile_system_info, Sfile_system_info, 1, 1, 0,
+ doc: /* Return storage information about the file system FILENAME is on.
+Value is a list of numbers (TOTAL FREE AVAIL), where TOTAL is the total
+storage of the file system, FREE is the free storage, and AVAIL is the
+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));
+
+ /* If the file name has special constructs in it,
+ call the corresponding file handler. */
+ Lisp_Object handler = Ffind_file_name_handler (encoded, Qfile_system_info);
+ if (!NILP (handler))
+ {
+ Lisp_Object result = call2 (handler, Qfile_system_info, encoded);
+ 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;
+ 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,
+ u.fsu_bavail_top_bit_set));
+}
+
+#endif /* !DOS_NT */
+
void
init_fileio (void)
{
@@ -5856,6 +5934,7 @@ syms_of_fileio (void)
DEFSYM (Qwrite_region, "write-region");
DEFSYM (Qverify_visited_file_modtime, "verify-visited-file-modtime");
DEFSYM (Qset_visited_file_modtime, "set-visited-file-modtime");
+ DEFSYM (Qfile_system_info, "file-system-info");
/* The symbol bound to coding-system-for-read when
insert-file-contents is called for recovering a file. This is not
@@ -6136,6 +6215,10 @@ This includes interactive calls to `delete-file' and
defsubr (&Sset_binary_mode);
+#ifndef DOS_NT
+ defsubr (&Sfile_system_info);
+#endif
+
#ifdef HAVE_SYNC
defsubr (&Sunix_sync);
#endif