]> git.eshelyaron.com Git - emacs.git/commitdiff
Work better if stat etc. are interrupted
authorPaul Eggert <eggert@cs.ucla.edu>
Mon, 20 Jan 2020 09:08:42 +0000 (01:08 -0800)
committerPaul Eggert <eggert@cs.ucla.edu>
Mon, 20 Jan 2020 09:21:52 +0000 (01:21 -0800)
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
src/fileio.c
src/filelock.c
src/lisp.h
src/lread.c
src/sysdep.c

index 611477aa4ef732da613ad7641c0d194bf487a28b..f013a4cea039b669a132b77d4a9303baa0fa3e1f 100644 (file)
@@ -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
index 34934dd6df64c2b7a7c31660e265606052776527..87a17eab425c2d86d91f61b83efb3a93c2f1d1e0 100644 (file)
@@ -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;
     }
 
index b28f16e9b5a6fc5d8e97211c4322889941fcd8d9..73202f0b2c462f53980c9056b25920cc795fdc24 100644 (file)
@@ -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;
index 4bcd1228443693fafc7eafbaa72372aaad91db0c..0bd375658e21255c58cc7a9d1b1efbae57febc5b 100644 (file)
@@ -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);
index 4e9860d5dc8addff271c8be81ae7c253d83c8072..69dd73912bc82e993af8aea9dd9b4b8333653e45 100644 (file)
@@ -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');
                 }
 
index c6344d8cec747e794789026ef8630f2c6b3a6d67..e8e8bbfb5024d3e8793c1025def1485f7869f955 100644 (file)
@@ -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 <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.
    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.  */