Commit b3ad638a authored by Paul Eggert's avatar Paul Eggert

Work better if stat etc. are interrupted

Quit or retry if fstat, lstat, stat or openat fail with EINTR.
This should fix some bugs on platforms where accessing files via
NFS can fail that way (Bug#9256).
* src/dired.c (file_attributes):
* src/fileio.c (file_directory_p) [O_PATH]:
Use emacs_openat instead of openat.
* src/dired.c (file_attributes): Use emacs_fstatat instead of fstatat.
* src/fileio.c (barf_or_query_if_file_exists, Frename_file):
* src/filelock.c (rename_lock_file):
Use emacs_fstatat instead of lstat.
* src/fileio.c (file_directory_p, Ffile_regular_p, Ffile_modes)
(Ffile_newer_than_file_p, Fverify_visited_file_modtime)
(Fset_visited_file_modtime, auto_save_1):
* src/lread.c (Fload):
* src/sysdep.c (get_current_dir_name_or_unreachable):
Use emacs_fstatat instead of stat.
* src/sysdep.c (emacs_fstatat, emacs_openat): New functions.
(emacs_open): Redo in terms of emacs_open.
parent 6cc1db81
Pipeline #4626 passed with stage
in 53 minutes and 51 seconds
...@@ -937,7 +937,7 @@ file_attributes (int fd, char const *name, ...@@ -937,7 +937,7 @@ file_attributes (int fd, char const *name,
int err = EINVAL; int err = EINVAL;
#if defined O_PATH && !defined HAVE_CYGWIN_O_PATH_BUG #if defined O_PATH && !defined HAVE_CYGWIN_O_PATH_BUG
int namefd = openat (fd, name, O_PATH | O_CLOEXEC | O_NOFOLLOW); int namefd = emacs_openat (fd, name, O_PATH | O_CLOEXEC | O_NOFOLLOW, 0);
if (namefd < 0) if (namefd < 0)
err = errno; err = errno;
else else
...@@ -970,7 +970,7 @@ file_attributes (int fd, char const *name, ...@@ -970,7 +970,7 @@ file_attributes (int fd, char const *name,
information to be accurate. */ information to be accurate. */
w32_stat_get_owner_group = 1; w32_stat_get_owner_group = 1;
#endif #endif
err = fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW) == 0 ? 0 : errno; err = emacs_fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW) == 0 ? 0 : errno;
#ifdef WINDOWSNT #ifdef WINDOWSNT
w32_stat_get_owner_group = 0; w32_stat_get_owner_group = 0;
#endif #endif
......
...@@ -1952,7 +1952,10 @@ barf_or_query_if_file_exists (Lisp_Object absname, bool known_to_exist, ...@@ -1952,7 +1952,10 @@ barf_or_query_if_file_exists (Lisp_Object absname, bool known_to_exist,
encoded_filename = ENCODE_FILE (absname); encoded_filename = ENCODE_FILE (absname);
if (! known_to_exist && lstat (SSDATA (encoded_filename), &statbuf) == 0) if (! known_to_exist
&& (emacs_fstatat (AT_FDCWD, SSDATA (encoded_filename),
&statbuf, AT_SYMLINK_NOFOLLOW)
== 0))
{ {
if (S_ISDIR (statbuf.st_mode)) if (S_ISDIR (statbuf.st_mode))
xsignal2 (Qfile_error, xsignal2 (Qfile_error,
...@@ -2555,7 +2558,9 @@ This is what happens in interactive use with M-x. */) ...@@ -2555,7 +2558,9 @@ This is what happens in interactive use with M-x. */)
bool dirp = !NILP (Fdirectory_name_p (file)); bool dirp = !NILP (Fdirectory_name_p (file));
if (!dirp) if (!dirp)
{ {
if (lstat (SSDATA (encoded_file), &file_st) != 0) if (emacs_fstatat (AT_FDCWD, SSDATA (encoded_file),
&file_st, AT_SYMLINK_NOFOLLOW)
!= 0)
report_file_error ("Renaming", list2 (file, newname)); report_file_error ("Renaming", list2 (file, newname));
dirp = S_ISDIR (file_st.st_mode) != 0; dirp = S_ISDIR (file_st.st_mode) != 0;
} }
...@@ -2928,7 +2933,8 @@ file_directory_p (Lisp_Object file) ...@@ -2928,7 +2933,8 @@ file_directory_p (Lisp_Object file)
#else #else
# ifdef O_PATH # ifdef O_PATH
/* Use O_PATH if available, as it avoids races and EOVERFLOW issues. */ /* 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); int fd = emacs_openat (AT_FDCWD, SSDATA (file),
O_PATH | O_CLOEXEC | O_DIRECTORY, 0);
if (0 <= fd) if (0 <= fd)
{ {
emacs_close (fd); emacs_close (fd);
...@@ -2939,9 +2945,9 @@ file_directory_p (Lisp_Object file) ...@@ -2939,9 +2945,9 @@ file_directory_p (Lisp_Object file)
/* O_PATH is defined but evidently this Linux kernel predates 2.6.39. /* O_PATH is defined but evidently this Linux kernel predates 2.6.39.
Fall back on generic POSIX code. */ Fall back on generic POSIX code. */
# endif # endif
/* Use file_accessible_directory_p, as it avoids stat EOVERFLOW /* Use file_accessible_directory_p, as it avoids fstatat EOVERFLOW
problems and could be cheaper. However, if it fails because FILE problems and could be cheaper. However, if it fails because FILE
is inaccessible, fall back on stat; if the latter fails with is inaccessible, fall back on fstatat; if the latter fails with
EOVERFLOW then FILE must have been a directory unless a race EOVERFLOW then FILE must have been a directory unless a race
condition occurred (a problem hard to work around portably). */ condition occurred (a problem hard to work around portably). */
if (file_accessible_directory_p (file)) if (file_accessible_directory_p (file))
...@@ -2949,7 +2955,7 @@ file_directory_p (Lisp_Object file) ...@@ -2949,7 +2955,7 @@ file_directory_p (Lisp_Object file)
if (errno != EACCES) if (errno != EACCES)
return false; return false;
struct stat st; struct stat st;
if (stat (SSDATA (file), &st) != 0) if (emacs_fstatat (AT_FDCWD, SSDATA (file), &st, 0) != 0)
return errno == EOVERFLOW; return errno == EOVERFLOW;
if (S_ISDIR (st.st_mode)) if (S_ISDIR (st.st_mode))
return true; return true;
...@@ -3080,7 +3086,7 @@ See `file-symlink-p' to distinguish symlinks. */) ...@@ -3080,7 +3086,7 @@ See `file-symlink-p' to distinguish symlinks. */)
Vw32_get_true_file_attributes = Qt; Vw32_get_true_file_attributes = Qt;
#endif #endif
int stat_result = stat (SSDATA (absname), &st); int stat_result = emacs_fstatat (AT_FDCWD, SSDATA (absname), &st, 0);
#ifdef WINDOWSNT #ifdef WINDOWSNT
Vw32_get_true_file_attributes = true_attributes; Vw32_get_true_file_attributes = true_attributes;
...@@ -3340,7 +3346,7 @@ Return nil if FILENAME does not exist. */) ...@@ -3340,7 +3346,7 @@ Return nil if FILENAME does not exist. */)
if (!NILP (handler)) if (!NILP (handler))
return call2 (handler, Qfile_modes, absname); return call2 (handler, Qfile_modes, absname);
if (stat (SSDATA (ENCODE_FILE (absname)), &st) != 0) if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname)), &st, 0) != 0)
return file_attribute_errno (absname, errno); return file_attribute_errno (absname, errno);
return make_fixnum (st.st_mode & 07777); return make_fixnum (st.st_mode & 07777);
} }
...@@ -3486,7 +3492,7 @@ otherwise, if FILE2 does not exist, the answer is t. */) ...@@ -3486,7 +3492,7 @@ otherwise, if FILE2 does not exist, the answer is t. */)
return call3 (handler, Qfile_newer_than_file_p, absname1, absname2); return call3 (handler, Qfile_newer_than_file_p, absname1, absname2);
int err1; int err1;
if (stat (SSDATA (ENCODE_FILE (absname1)), &st1) == 0) if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname1)), &st1, 0) == 0)
err1 = 0; err1 = 0;
else else
{ {
...@@ -3494,7 +3500,7 @@ otherwise, if FILE2 does not exist, the answer is t. */) ...@@ -3494,7 +3500,7 @@ otherwise, if FILE2 does not exist, the answer is t. */)
if (err1 != EOVERFLOW) if (err1 != EOVERFLOW)
return file_attribute_errno (absname1, err1); return file_attribute_errno (absname1, err1);
} }
if (stat (SSDATA (ENCODE_FILE (absname2)), &st2) != 0) if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname2)), &st2, 0) != 0)
{ {
file_attribute_errno (absname2, errno); file_attribute_errno (absname2, errno);
return Qt; return Qt;
...@@ -3880,7 +3886,7 @@ by calling `format-decode', which see. */) ...@@ -3880,7 +3886,7 @@ by calling `format-decode', which see. */)
if (end_offset < 0) if (end_offset < 0)
buffer_overflow (); buffer_overflow ();
/* The file size returned from stat may be zero, but data /* The file size returned from fstat may be zero, but data
may be readable nonetheless, for example when this is a may be readable nonetheless, for example when this is a
file in the /proc filesystem. */ file in the /proc filesystem. */
if (end_offset == 0) if (end_offset == 0)
...@@ -5625,7 +5631,7 @@ See Info node `(elisp)Modification Time' for more details. */) ...@@ -5625,7 +5631,7 @@ See Info node `(elisp)Modification Time' for more details. */)
filename = ENCODE_FILE (BVAR (b, filename)); filename = ENCODE_FILE (BVAR (b, filename));
mtime = (stat (SSDATA (filename), &st) == 0 mtime = (emacs_fstatat (AT_FDCWD, SSDATA (filename), &st, 0) == 0
? get_stat_mtime (&st) ? get_stat_mtime (&st)
: time_error_value (errno)); : time_error_value (errno));
if (timespec_cmp (mtime, b->modtime) == 0 if (timespec_cmp (mtime, b->modtime) == 0
...@@ -5689,7 +5695,8 @@ in `current-time' or an integer flag as returned by `visited-file-modtime'. */) ...@@ -5689,7 +5695,8 @@ 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. */ /* The handler can find the file name the same way we did. */
return call2 (handler, Qset_visited_file_modtime, Qnil); return call2 (handler, Qset_visited_file_modtime, Qnil);
if (stat (SSDATA (ENCODE_FILE (filename)), &st) == 0) if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (filename)), &st, 0)
== 0)
{ {
current_buffer->modtime = get_stat_mtime (&st); current_buffer->modtime = get_stat_mtime (&st);
current_buffer->modtime_size = st.st_size; current_buffer->modtime_size = st.st_size;
...@@ -5728,12 +5735,14 @@ auto_save_1 (void) ...@@ -5728,12 +5735,14 @@ auto_save_1 (void)
/* Get visited file's mode to become the auto save file's mode. */ /* Get visited file's mode to become the auto save file's mode. */
if (! NILP (BVAR (current_buffer, filename))) if (! NILP (BVAR (current_buffer, filename)))
{ {
if (stat (SSDATA (BVAR (current_buffer, filename)), &st) >= 0) if (emacs_fstatat (AT_FDCWD, SSDATA (BVAR (current_buffer, filename)),
&st, 0)
== 0)
/* But make sure we can overwrite it later! */ /* But make sure we can overwrite it later! */
auto_save_mode_bits = (st.st_mode | 0600) & 0777; auto_save_mode_bits = (st.st_mode | 0600) & 0777;
else if (modes = Ffile_modes (BVAR (current_buffer, filename)), else if (modes = Ffile_modes (BVAR (current_buffer, filename)),
FIXNUMP (modes)) FIXNUMP (modes))
/* Remote files don't cooperate with stat. */ /* Remote files don't cooperate with fstatat. */
auto_save_mode_bits = (XFIXNUM (modes) | 0600) & 0777; auto_save_mode_bits = (XFIXNUM (modes) | 0600) & 0777;
} }
......
...@@ -347,7 +347,8 @@ rename_lock_file (char const *old, char const *new, bool force) ...@@ -347,7 +347,8 @@ rename_lock_file (char const *old, char const *new, bool force)
potential race condition since some other process may create potential race condition since some other process may create
NEW immediately after the existence check, but it's the best NEW immediately after the existence check, but it's the best
we can portably do here. */ we can portably do here. */
if (lstat (new, &st) == 0 || errno == EOVERFLOW) if (emacs_fstatat (AT_FDCWD, new, &st, AT_SYMLINK_NOFOLLOW) == 0
|| errno == EOVERFLOW)
{ {
errno = EEXIST; errno = EEXIST;
return -1; return -1;
......
...@@ -4605,6 +4605,8 @@ extern void seed_random (void *, ptrdiff_t); ...@@ -4605,6 +4605,8 @@ extern void seed_random (void *, ptrdiff_t);
extern void init_random (void); extern void init_random (void);
extern void emacs_backtrace (int); extern void emacs_backtrace (int);
extern AVOID emacs_abort (void) NO_INLINE; extern AVOID emacs_abort (void) NO_INLINE;
extern int emacs_fstatat (int, char const *, void *, int);
extern int emacs_openat (int, char const *, int, int);
extern int emacs_open (const char *, int, int); extern int emacs_open (const char *, int, int);
extern int emacs_pipe (int[2]); extern int emacs_pipe (int[2]);
extern int emacs_close (int); extern int emacs_close (int);
......
...@@ -1353,11 +1353,11 @@ Return t if the file exists and loads successfully. */) ...@@ -1353,11 +1353,11 @@ Return t if the file exists and loads successfully. */)
ignores suffix order due to load_prefer_newer. */ ignores suffix order due to load_prefer_newer. */
if (!load_prefer_newer && is_elc) if (!load_prefer_newer && is_elc)
{ {
result = stat (SSDATA (efound), &s1); result = emacs_fstatat (AT_FDCWD, SSDATA (efound), &s1, 0);
if (result == 0) if (result == 0)
{ {
SSET (efound, SBYTES (efound) - 1, 0); SSET (efound, SBYTES (efound) - 1, 0);
result = stat (SSDATA (efound), &s2); result = emacs_fstatat (AT_FDCWD, SSDATA (efound), &s2, 0);
SSET (efound, SBYTES (efound) - 1, 'c'); SSET (efound, SBYTES (efound) - 1, 'c');
} }
......
...@@ -312,8 +312,8 @@ get_current_dir_name_or_unreachable (void) ...@@ -312,8 +312,8 @@ get_current_dir_name_or_unreachable (void)
if (pwd if (pwd
&& (pwdlen = strnlen (pwd, bufsize_max)) < bufsize_max && (pwdlen = strnlen (pwd, bufsize_max)) < bufsize_max
&& IS_DIRECTORY_SEP (pwd[pwdlen && IS_DEVICE_SEP (pwd[1]) ? 2 : 0]) && IS_DIRECTORY_SEP (pwd[pwdlen && IS_DEVICE_SEP (pwd[1]) ? 2 : 0])
&& stat (pwd, &pwdstat) == 0 && emacs_fstatat (AT_FDCWD, pwd, &pwdstat, 0) == 0
&& stat (".", &dotstat) == 0 && emacs_fstatat (AT_FDCWD, ".", &dotstat, 0) == 0
&& dotstat.st_ino == pwdstat.st_ino && dotstat.st_ino == pwdstat.st_ino
&& dotstat.st_dev == pwdstat.st_dev) && dotstat.st_dev == pwdstat.st_dev)
{ {
...@@ -2449,7 +2449,27 @@ emacs_abort (void) ...@@ -2449,7 +2449,27 @@ emacs_abort (void)
} }
#endif #endif
/* Open FILE for Emacs use, using open flags OFLAG and mode MODE. /* Assuming the directory DIRFD, store information about FILENAME into *ST,
using FLAGS to control how the status is obtained.
Do not fail merely because fetching info was interrupted by a signal.
Allow the user to quit.
The type of ST is void * instead of struct stat * because the
latter type would be problematic in lisp.h. Some platforms may
play tricks like "#define stat stat64" in <sys/stat.h>, and lisp.h
does not include <sys/stat.h>. */
int
emacs_fstatat (int dirfd, char const *filename, void *st, int flags)
{
int r;
while ((r = fstatat (dirfd, filename, st, flags)) != 0 && errno == EINTR)
maybe_quit ();
return r;
}
/* Assuming the directory DIRFD, open FILE for Emacs use,
using open flags OFLAGS and mode MODE.
Use binary I/O on systems that care about text vs binary I/O. Use binary I/O on systems that care about text vs binary I/O.
Arrange for subprograms to not inherit the file descriptor. Arrange for subprograms to not inherit the file descriptor.
Prefer a method that is multithread-safe, if available. Prefer a method that is multithread-safe, if available.
...@@ -2457,17 +2477,23 @@ emacs_abort (void) ...@@ -2457,17 +2477,23 @@ emacs_abort (void)
Allow the user to quit. */ Allow the user to quit. */
int int
emacs_open (const char *file, int oflags, int mode) emacs_openat (int dirfd, char const *file, int oflags, int mode)
{ {
int fd; int fd;
if (! (oflags & O_TEXT)) if (! (oflags & O_TEXT))
oflags |= O_BINARY; oflags |= O_BINARY;
oflags |= O_CLOEXEC; oflags |= O_CLOEXEC;
while ((fd = open (file, oflags, mode)) < 0 && errno == EINTR) while ((fd = openat (dirfd, file, oflags, mode)) < 0 && errno == EINTR)
maybe_quit (); maybe_quit ();
return fd; return fd;
} }
int
emacs_open (char const *file, int oflags, int mode)
{
return emacs_openat (AT_FDCWD, file, oflags, mode);
}
/* Open FILE as a stream for Emacs use, with mode MODE. /* Open FILE as a stream for Emacs use, with mode MODE.
Act like emacs_open with respect to threads, signals, and quits. */ Act like emacs_open with respect to threads, signals, and quits. */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment