From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.ciao.gmane.io!not-for-mail From: Paul Eggert Newsgroups: gmane.emacs.bugs Subject: bug#9256: Erroneous output from "verify-visited-file-modtime" (fileio.c) Date: Mon, 20 Jan 2020 01:36:24 -0800 Organization: UCLA Computer Science Department Message-ID: References: <874o1vuenv.fsf@data.fbx.proxad.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------58E8C3775B6924EB35AEDE1E" Injection-Info: ciao.gmane.io; posting-host="ciao.gmane.io:159.69.161.202"; logging-data="16532"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.4.1 Cc: Vivien Mallet , 9256-done@debbugs.gnu.org To: Lars Ingebrigtsen Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Mon Jan 20 10:37:19 2020 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1itTUk-00046I-NI for geb-bug-gnu-emacs@m.gmane-mx.org; Mon, 20 Jan 2020 10:37:18 +0100 Original-Received: from localhost ([::1]:60836 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1itTUj-0007s6-GC for geb-bug-gnu-emacs@m.gmane-mx.org; Mon, 20 Jan 2020 04:37:17 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:57633) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1itTUY-0007pj-Og for bug-gnu-emacs@gnu.org; Mon, 20 Jan 2020 04:37:10 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1itTUU-00060I-Ub for bug-gnu-emacs@gnu.org; Mon, 20 Jan 2020 04:37:06 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]:38421) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1itTUU-000608-Mt for bug-gnu-emacs@gnu.org; Mon, 20 Jan 2020 04:37:02 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1itTUU-0003AK-LM for bug-gnu-emacs@gnu.org; Mon, 20 Jan 2020 04:37:02 -0500 In-Reply-To: <874o1vuenv.fsf@data.fbx.proxad.net> Resent-From: Paul Eggert Original-Sender: "Debbugs-submit" Resent-To: bug-gnu-emacs@gnu.org Resent-Date: Mon, 20 Jan 2020 09:37:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: cc-closed 9256 X-GNU-PR-Package: emacs Mail-Followup-To: 9256@debbugs.gnu.org, eggert@cs.ucla.edu, Vivien.Mallet@inria.fr Original-Received: via spool by 9256-done@debbugs.gnu.org id=D9256.157951300212137 (code D ref 9256); Mon, 20 Jan 2020 09:37:02 +0000 Original-Received: (at 9256-done) by debbugs.gnu.org; 20 Jan 2020 09:36:42 +0000 Original-Received: from localhost ([127.0.0.1]:44394 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1itTU6-00039d-Eh for submit@debbugs.gnu.org; Mon, 20 Jan 2020 04:36:42 -0500 Original-Received: from zimbra.cs.ucla.edu ([131.179.128.68]:34116) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1itTU1-00039N-AC for 9256-done@debbugs.gnu.org; Mon, 20 Jan 2020 04:36:37 -0500 Original-Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id 3923516007E; Mon, 20 Jan 2020 01:36:27 -0800 (PST) Original-Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id 2SeSWedDse_A; Mon, 20 Jan 2020 01:36:25 -0800 (PST) Original-Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id 798EC160086; Mon, 20 Jan 2020 01:36:25 -0800 (PST) X-Virus-Scanned: amavisd-new at zimbra.cs.ucla.edu Original-Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id fpJ5Pw0P6Aec; Mon, 20 Jan 2020 01:36:25 -0800 (PST) Original-Received: from [192.168.1.9] (cpe-23-242-74-103.socal.res.rr.com [23.242.74.103]) by zimbra.cs.ucla.edu (Postfix) with ESMTPSA id 46C5116007E; Mon, 20 Jan 2020 01:36:25 -0800 (PST) Content-Language: en-US X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.51.188.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:174905 Archived-At: This is a multi-part message in MIME format. --------------58E8C3775B6924EB35AEDE1E Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit > I'd expect things to bug out pretty regularly across the board, > since you'd have to check for EINTR in every single call to a bunch of > system calls Yes in theory. However, Emacs already does the EINTR check for open, read and write even on regular files where POSIX says it can't happen (but it does happen with NFS). If you've recently dealt with an NFS file then it'll be cached on the client and you won't get EINTR, so in practice the issue comes up only for syscalls that are applied to a file that hasn't been looked at lately. stat is one of these calls (hence the bug report) so we might as well do the EINTR check for it as well. I installed the attached patch to do that for stat and similar calls, and also for openat (which I think was overlooked when 'open' was done). The other part of this bug report (with ENOENT) is not something Emacs can work around and it's surely a bug in the Linux NFS client that was most likely fixed a while ago anyway . As I think both issues in the bug report have been addressed, I'm boldly closing it. --------------58E8C3775B6924EB35AEDE1E Content-Type: text/x-patch; charset=UTF-8; name="0001-Work-better-if-stat-etc.-are-interrupted.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0001-Work-better-if-stat-etc.-are-interrupted.patch" >From b3ad638a60845f17938ff812efcf2b2edfbd8c57 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Mon, 20 Jan 2020 01:08:42 -0800 Subject: [PATCH] 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. --- src/dired.c | 4 ++-- src/fileio.c | 39 ++++++++++++++++++++++++--------------- src/filelock.c | 3 ++- src/lisp.h | 2 ++ src/lread.c | 4 ++-- src/sysdep.c | 36 +++++++++++++++++++++++++++++++----- 6 files changed, 63 insertions(+), 25 deletions(-) diff --git a/src/dired.c b/src/dired.c index 611477aa4e..f013a4cea0 100644 --- a/src/dired.c +++ b/src/dired.c @@ -937,7 +937,7 @@ file_attributes (int fd, char const *name, int err = EINVAL; #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) err = errno; else @@ -970,7 +970,7 @@ file_attributes (int fd, char const *name, information to be accurate. */ w32_stat_get_owner_group = 1; #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 w32_stat_get_owner_group = 0; #endif diff --git a/src/fileio.c b/src/fileio.c index 34934dd6df..87a17eab42 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -1952,7 +1952,10 @@ barf_or_query_if_file_exists (Lisp_Object absname, bool known_to_exist, 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)) xsignal2 (Qfile_error, @@ -2555,7 +2558,9 @@ This is what happens in interactive use with M-x. */) bool dirp = !NILP (Fdirectory_name_p (file)); 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)); dirp = S_ISDIR (file_st.st_mode) != 0; } @@ -2928,7 +2933,8 @@ file_directory_p (Lisp_Object file) #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); + int fd = emacs_openat (AT_FDCWD, SSDATA (file), + O_PATH | O_CLOEXEC | O_DIRECTORY, 0); if (0 <= fd) { emacs_close (fd); @@ -2939,9 +2945,9 @@ 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_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 - 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 condition occurred (a problem hard to work around portably). */ if (file_accessible_directory_p (file)) @@ -2949,7 +2955,7 @@ file_directory_p (Lisp_Object file) if (errno != EACCES) return false; struct stat st; - if (stat (SSDATA (file), &st) != 0) + if (emacs_fstatat (AT_FDCWD, SSDATA (file), &st, 0) != 0) return errno == EOVERFLOW; if (S_ISDIR (st.st_mode)) return true; @@ -3080,7 +3086,7 @@ See `file-symlink-p' to distinguish symlinks. */) Vw32_get_true_file_attributes = Qt; #endif - int stat_result = stat (SSDATA (absname), &st); + int stat_result = emacs_fstatat (AT_FDCWD, SSDATA (absname), &st, 0); #ifdef WINDOWSNT Vw32_get_true_file_attributes = true_attributes; @@ -3340,7 +3346,7 @@ Return nil if FILENAME does not exist. */) if (!NILP (handler)) 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 make_fixnum (st.st_mode & 07777); } @@ -3486,7 +3492,7 @@ otherwise, if FILE2 does not exist, the answer is t. */) return call3 (handler, Qfile_newer_than_file_p, absname1, absname2); int err1; - if (stat (SSDATA (ENCODE_FILE (absname1)), &st1) == 0) + if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname1)), &st1, 0) == 0) err1 = 0; else { @@ -3494,7 +3500,7 @@ otherwise, if FILE2 does not exist, the answer is t. */) if (err1 != EOVERFLOW) 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); return Qt; @@ -3880,7 +3886,7 @@ by calling `format-decode', which see. */) if (end_offset < 0) 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 file in the /proc filesystem. */ if (end_offset == 0) @@ -5625,7 +5631,7 @@ See Info node `(elisp)Modification Time' for more details. */) 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) : time_error_value (errno)); if (timespec_cmp (mtime, b->modtime) == 0 @@ -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. */ 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_size = st.st_size; @@ -5728,12 +5735,14 @@ auto_save_1 (void) /* Get visited file's mode to become the auto save file's mode. */ 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! */ auto_save_mode_bits = (st.st_mode | 0600) & 0777; else if (modes = Ffile_modes (BVAR (current_buffer, filename)), FIXNUMP (modes)) - /* Remote files don't cooperate with stat. */ + /* Remote files don't cooperate with fstatat. */ auto_save_mode_bits = (XFIXNUM (modes) | 0600) & 0777; } diff --git a/src/filelock.c b/src/filelock.c index b28f16e9b5..73202f0b2c 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -347,7 +347,8 @@ rename_lock_file (char const *old, char const *new, bool force) potential race condition since some other process may create NEW immediately after the existence check, but it's the best 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; return -1; diff --git a/src/lisp.h b/src/lisp.h index 4bcd122844..0bd375658e 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4605,6 +4605,8 @@ extern void seed_random (void *, ptrdiff_t); extern void init_random (void); extern void emacs_backtrace (int); 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_pipe (int[2]); extern int emacs_close (int); diff --git a/src/lread.c b/src/lread.c index 4e9860d5dc..69dd73912b 100644 --- a/src/lread.c +++ b/src/lread.c @@ -1353,11 +1353,11 @@ Return t if the file exists and loads successfully. */) ignores suffix order due to load_prefer_newer. */ if (!load_prefer_newer && is_elc) { - result = stat (SSDATA (efound), &s1); + result = emacs_fstatat (AT_FDCWD, SSDATA (efound), &s1, 0); if (result == 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'); } diff --git a/src/sysdep.c b/src/sysdep.c index c6344d8cec..e8e8bbfb50 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -312,8 +312,8 @@ get_current_dir_name_or_unreachable (void) if (pwd && (pwdlen = strnlen (pwd, bufsize_max)) < bufsize_max && IS_DIRECTORY_SEP (pwd[pwdlen && IS_DEVICE_SEP (pwd[1]) ? 2 : 0]) - && stat (pwd, &pwdstat) == 0 - && stat (".", &dotstat) == 0 + && emacs_fstatat (AT_FDCWD, pwd, &pwdstat, 0) == 0 + && emacs_fstatat (AT_FDCWD, ".", &dotstat, 0) == 0 && dotstat.st_ino == pwdstat.st_ino && dotstat.st_dev == pwdstat.st_dev) { @@ -2449,7 +2449,27 @@ emacs_abort (void) } #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 , and lisp.h + does not include . */ + +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. Arrange for subprograms to not inherit the file descriptor. Prefer a method that is multithread-safe, if available. @@ -2457,17 +2477,23 @@ emacs_abort (void) Allow the user to quit. */ int -emacs_open (const char *file, int oflags, int mode) +emacs_openat (int dirfd, char const *file, int oflags, int mode) { int fd; if (! (oflags & O_TEXT)) oflags |= O_BINARY; oflags |= O_CLOEXEC; - while ((fd = open (file, oflags, mode)) < 0 && errno == EINTR) + while ((fd = openat (dirfd, file, oflags, mode)) < 0 && errno == EINTR) maybe_quit (); 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. Act like emacs_open with respect to threads, signals, and quits. */ -- 2.17.1 --------------58E8C3775B6924EB35AEDE1E--