aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2018-02-03 12:10:19 -0800
committerPaul Eggert2018-02-03 12:19:24 -0800
commit327d251f8a857350a78029c31c7ab3f9797cc727 (patch)
tree06cf0b606cb68a3576960fe1befab7aa188777d5 /src
parenta34c7d7470d5f749659658943bd4084942d873e3 (diff)
downloademacs-327d251f8a857350a78029c31c7ab3f9797cc727.tar.gz
emacs-327d251f8a857350a78029c31c7ab3f9797cc727.zip
Avoid EOVERFLOW problems with file-directory-p
This fixes a bug where (file-directory-p FOO) would fail if FOO had an inode number out of range for ‘stat’. * src/fileio.c (file_directory_p): Accept a Lisp string instead of a C string. All callers changed. On non-MS-Windows hosts, use openat with O_PATH|O_DIRECTORY if available, otherwise file_accessible_directory_p unless it fails due to EACCESS, otherwise stat.
Diffstat (limited to 'src')
-rw-r--r--src/fileio.c46
-rw-r--r--src/lisp.h2
-rw-r--r--src/lread.c2
3 files changed, 39 insertions, 11 deletions
diff --git a/src/fileio.c b/src/fileio.c
index 62f641fdea2..be29e60fc0a 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -139,7 +139,7 @@ static bool e_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
139 struct coding_system *); 139 struct coding_system *);
140 140
141 141
142/* Return true if FILENAME exists. */ 142/* Return true if FILENAME exists, otherwise return false and set errno. */
143 143
144static bool 144static bool
145check_existing (const char *filename) 145check_existing (const char *filename)
@@ -2595,7 +2595,7 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
2595 /* The read-only attribute of the parent directory doesn't affect 2595 /* The read-only attribute of the parent directory doesn't affect
2596 whether a file or directory can be created within it. Some day we 2596 whether a file or directory can be created within it. Some day we
2597 should check ACLs though, which do affect this. */ 2597 should check ACLs though, which do affect this. */
2598 return file_directory_p (SSDATA (dir)) ? Qt : Qnil; 2598 return file_directory_p (dir) ? Qt : Qnil;
2599#else 2599#else
2600 return check_writable (SSDATA (dir), W_OK | X_OK) ? Qt : Qnil; 2600 return check_writable (SSDATA (dir), W_OK | X_OK) ? Qt : Qnil;
2601#endif 2601#endif
@@ -2689,19 +2689,47 @@ See `file-symlink-p' to distinguish symlinks. */)
2689 2689
2690 absname = ENCODE_FILE (absname); 2690 absname = ENCODE_FILE (absname);
2691 2691
2692 return file_directory_p (SSDATA (absname)) ? Qt : Qnil; 2692 return file_directory_p (absname) ? Qt : Qnil;
2693} 2693}
2694 2694
2695/* Return true if FILE is a directory or a symlink to a directory. */ 2695/* Return true if FILE is a directory or a symlink to a directory.
2696 Otherwise return false and set errno. */
2696bool 2697bool
2697file_directory_p (char const *file) 2698file_directory_p (Lisp_Object file)
2698{ 2699{
2699#ifdef WINDOWSNT 2700#ifdef WINDOWSNT
2700 /* This is cheaper than 'stat'. */ 2701 /* This is cheaper than 'stat'. */
2701 return faccessat (AT_FDCWD, file, D_OK, AT_EACCESS) == 0; 2702 return faccessat (AT_FDCWD, SSDATA (file), D_OK, AT_EACCESS) == 0;
2702#else 2703#else
2704# ifdef O_PATH
2705 /* Use O_PATH if available, as it avoids races and EOVERFLOW issues. */
2706 int fd = openat (AT_FDCWD, SSDATA (file), O_PATH | O_CLOEXEC | O_DIRECTORY);
2707 if (0 <= fd)
2708 {
2709 emacs_close (fd);
2710 return true;
2711 }
2712 if (errno != EINVAL)
2713 return false;
2714 /* O_PATH is defined but evidently this Linux kernel predates 2.6.39.
2715 Fall back on generic POSIX code. */
2716# endif
2717 /* Use file_accessible_directory, as it avoids stat EOVERFLOW
2718 problems and could be cheaper. However, if it fails because FILE
2719 is inaccessible, fall back on stat; if the latter fails with
2720 EOVERFLOW then FILE must have been a directory unless a race
2721 condition occurred (a problem hard to work around portably). */
2722 if (file_accessible_directory_p (file))
2723 return true;
2724 if (errno != EACCES)
2725 return false;
2703 struct stat st; 2726 struct stat st;
2704 return stat (file, &st) == 0 && S_ISDIR (st.st_mode); 2727 if (stat (SSDATA (file), &st) != 0)
2728 return errno == EOVERFLOW;
2729 if (S_ISDIR (st.st_mode))
2730 return true;
2731 errno = ENOTDIR;
2732 return false;
2705#endif 2733#endif
2706} 2734}
2707 2735
@@ -2762,7 +2790,7 @@ file_accessible_directory_p (Lisp_Object file)
2762 return (SBYTES (file) == 0 2790 return (SBYTES (file) == 0
2763 || w32_accessible_directory_p (SSDATA (file), SBYTES (file))); 2791 || w32_accessible_directory_p (SSDATA (file), SBYTES (file)));
2764# else /* MSDOS */ 2792# else /* MSDOS */
2765 return file_directory_p (SSDATA (file)); 2793 return file_directory_p (file);
2766# endif /* MSDOS */ 2794# endif /* MSDOS */
2767#else /* !DOS_NT */ 2795#else /* !DOS_NT */
2768 /* On POSIXish platforms, use just one system call; this avoids a 2796 /* On POSIXish platforms, use just one system call; this avoids a
@@ -3192,7 +3220,7 @@ Use the current time if TIMESTAMP is nil. TIMESTAMP is in the format of
3192 { 3220 {
3193#ifdef MSDOS 3221#ifdef MSDOS
3194 /* Setting times on a directory always fails. */ 3222 /* Setting times on a directory always fails. */
3195 if (file_directory_p (SSDATA (encoded_absname))) 3223 if (file_directory_p (encoded_absname))
3196 return Qnil; 3224 return Qnil;
3197#endif 3225#endif
3198 report_file_error ("Setting file times", absname); 3226 report_file_error ("Setting file times", absname);
diff --git a/src/lisp.h b/src/lisp.h
index d547c0c4430..a7f0a1d78ff 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4130,7 +4130,7 @@ extern _Noreturn void report_file_error (const char *, Lisp_Object);
4130extern _Noreturn void report_file_notify_error (const char *, Lisp_Object); 4130extern _Noreturn void report_file_notify_error (const char *, Lisp_Object);
4131extern bool internal_delete_file (Lisp_Object); 4131extern bool internal_delete_file (Lisp_Object);
4132extern Lisp_Object emacs_readlinkat (int, const char *); 4132extern Lisp_Object emacs_readlinkat (int, const char *);
4133extern bool file_directory_p (const char *); 4133extern bool file_directory_p (Lisp_Object);
4134extern bool file_accessible_directory_p (Lisp_Object); 4134extern bool file_accessible_directory_p (Lisp_Object);
4135extern void init_fileio (void); 4135extern void init_fileio (void);
4136extern void syms_of_fileio (void); 4136extern void syms_of_fileio (void);
diff --git a/src/lread.c b/src/lread.c
index 1221dc9a05f..7cacd47d510 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1702,7 +1702,7 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
1702 AT_EACCESS) 1702 AT_EACCESS)
1703 == 0) 1703 == 0)
1704 { 1704 {
1705 if (file_directory_p (pfn)) 1705 if (file_directory_p (encoded_fn))
1706 last_errno = EISDIR; 1706 last_errno = EISDIR;
1707 else 1707 else
1708 fd = 1; 1708 fd = 1;