diff options
| author | Paul Eggert | 2018-02-03 12:10:19 -0800 |
|---|---|---|
| committer | Paul Eggert | 2018-02-03 12:19:24 -0800 |
| commit | 327d251f8a857350a78029c31c7ab3f9797cc727 (patch) | |
| tree | 06cf0b606cb68a3576960fe1befab7aa188777d5 /src | |
| parent | a34c7d7470d5f749659658943bd4084942d873e3 (diff) | |
| download | emacs-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.c | 46 | ||||
| -rw-r--r-- | src/lisp.h | 2 | ||||
| -rw-r--r-- | src/lread.c | 2 |
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 | ||
| 144 | static bool | 144 | static bool |
| 145 | check_existing (const char *filename) | 145 | check_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. */ | ||
| 2696 | bool | 2697 | bool |
| 2697 | file_directory_p (char const *file) | 2698 | file_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); | |||
| 4130 | extern _Noreturn void report_file_notify_error (const char *, Lisp_Object); | 4130 | extern _Noreturn void report_file_notify_error (const char *, Lisp_Object); |
| 4131 | extern bool internal_delete_file (Lisp_Object); | 4131 | extern bool internal_delete_file (Lisp_Object); |
| 4132 | extern Lisp_Object emacs_readlinkat (int, const char *); | 4132 | extern Lisp_Object emacs_readlinkat (int, const char *); |
| 4133 | extern bool file_directory_p (const char *); | 4133 | extern bool file_directory_p (Lisp_Object); |
| 4134 | extern bool file_accessible_directory_p (Lisp_Object); | 4134 | extern bool file_accessible_directory_p (Lisp_Object); |
| 4135 | extern void init_fileio (void); | 4135 | extern void init_fileio (void); |
| 4136 | extern void syms_of_fileio (void); | 4136 | extern 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; |