bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] stat: work around Solaris bug with tv_nsec < 0


From: Paul Eggert
Subject: [PATCH] stat: work around Solaris bug with tv_nsec < 0
Date: Thu, 23 Nov 2017 00:07:10 -0800

* doc/posix-functions/fstat.texi (fstat):
* doc/posix-functions/fstatat.texi (fstatat):
* doc/posix-functions/lstat.texi (lstat):
* doc/posix-functions/stat.texi (stat):
Mention Solaris 11 bug.
* lib/fstat.c, lib/fstatat.c, lib/lstat.c: Include stat-time.h.
* lib/fstat.c (rpl_fstat) [!WINDOWS_NATIVE]:
* lib/lstat.c (rpl_lstat):
* lib/stat.c (rpl_stat):
Normalize resulting timestamps.
* lib/fstatat.c (normal_fstatat): New function.
(rpl_fstatat): Use it.
* lib/stat-time.h: Include intprops.h, errno.h, stddef.h.
(stat_time_normalize): New function.
* m4/fstat.m4 (gl_FUNC_FSTAT):
* m4/fstatat.m4 (gl_FUNC_FSTATAT):
* m4/lstat.m4 (gl_FUNC_LSTAT):
* m4/stat.m4 (gl_FUNC_STAT):
Replace on Solaris.
* modules/fstat (Depends-on):
* modules/fstatat (Depends-on):
Add stat-time.
* modules/stat-time (Depends-on): Add errno, intprops.
---
 ChangeLog                        | 27 ++++++++++++++++++++++++
 doc/posix-functions/fstat.texi   |  5 +++++
 doc/posix-functions/fstatat.texi |  5 +++++
 doc/posix-functions/lstat.texi   |  5 +++++
 doc/posix-functions/stat.texi    |  5 +++++
 lib/fstat.c                      |  4 +++-
 lib/fstatat.c                    | 12 +++++++++--
 lib/lstat.c                      | 39 ++++++++++++++++++----------------
 lib/stat-time.h                  | 45 ++++++++++++++++++++++++++++++++++++++++
 lib/stat.c                       | 22 ++++++++++++--------
 m4/fstat.m4                      |  7 ++++---
 m4/fstatat.m4                    | 19 ++++++++++-------
 m4/lstat.m4                      |  7 ++++---
 m4/stat.m4                       |  7 ++++++-
 modules/fstat                    |  1 +
 modules/fstatat                  |  1 +
 modules/stat-time                |  2 ++
 17 files changed, 169 insertions(+), 44 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index edcb62d08..700ee09c5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2017-11-23  Paul Eggert  <address@hidden>
+
+       stat: work around Solaris bug with tv_nsec < 0
+       * doc/posix-functions/fstat.texi (fstat):
+       * doc/posix-functions/fstatat.texi (fstatat):
+       * doc/posix-functions/lstat.texi (lstat):
+       * doc/posix-functions/stat.texi (stat):
+       Mention Solaris 11 bug.
+       * lib/fstat.c, lib/fstatat.c, lib/lstat.c: Include stat-time.h.
+       * lib/fstat.c (rpl_fstat) [!WINDOWS_NATIVE]:
+       * lib/lstat.c (rpl_lstat):
+       * lib/stat.c (rpl_stat):
+       Normalize resulting timestamps.
+       * lib/fstatat.c (normal_fstatat): New function.
+       (rpl_fstatat): Use it.
+       * lib/stat-time.h: Include intprops.h, errno.h, stddef.h.
+       (stat_time_normalize): New function.
+       * m4/fstat.m4 (gl_FUNC_FSTAT):
+       * m4/fstatat.m4 (gl_FUNC_FSTATAT):
+       * m4/lstat.m4 (gl_FUNC_LSTAT):
+       * m4/stat.m4 (gl_FUNC_STAT):
+       Replace on Solaris.
+       * modules/fstat (Depends-on):
+       * modules/fstatat (Depends-on):
+       Add stat-time.
+       * modules/stat-time (Depends-on): Add errno, intprops.
+
 2017-11-22  Paul Eggert  <address@hidden>
 
        regex: merge from glibc
diff --git a/doc/posix-functions/fstat.texi b/doc/posix-functions/fstat.texi
index 494889cb5..65dfd2182 100644
--- a/doc/posix-functions/fstat.texi
+++ b/doc/posix-functions/fstat.texi
@@ -16,6 +16,11 @@ On platforms where @code{off_t} is a 32-bit type, 
@code{fstat} may not correctly
 report the size of files or block devices larger than 2 GB.
 (Cf. @code{AC_SYS_LARGEFILE}.)
 @item
+On Solaris 11.3, when this function yields a timestamp with a
+nonpositive @code{tv_sec} value, @code{tv_nsec} might in the range
address@hidden@minus{}1, representing a negative nanoseconds
+offset from @code{tv_sec}.
address@hidden
 The @code{st_atime}, @code{st_ctime}, @code{st_mtime} fields are affected by
 the current time zone and by the DST flag of the current time zone on some
 platforms:
diff --git a/doc/posix-functions/fstatat.texi b/doc/posix-functions/fstatat.texi
index 1eaadcb81..69cd62b47 100644
--- a/doc/posix-functions/fstatat.texi
+++ b/doc/posix-functions/fstatat.texi
@@ -25,6 +25,11 @@ Solaris 9.
 For symlinks, when the argument ends in a slash, some platforms don't
 dereference the argument:
 Solaris 9.
address@hidden
+On Solaris 11.3, when this function yields a timestamp with a
+nonpositive @code{tv_sec} value, @code{tv_nsec} might in the range
address@hidden@minus{}1, representing a negative nanoseconds
+offset from @code{tv_sec}.
 @end itemize
 
 Portability problems not fixed by Gnulib:
diff --git a/doc/posix-functions/lstat.texi b/doc/posix-functions/lstat.texi
index 6f5cfc25c..9c7e85ec6 100644
--- a/doc/posix-functions/lstat.texi
+++ b/doc/posix-functions/lstat.texi
@@ -21,6 +21,11 @@ On some platforms, @code{lstat("file/",buf)} succeeds 
instead of
 failing with @code{ENOTDIR}.
 Solaris 9.
 @item
+On Solaris 11.3, when this function yields a timestamp with a
+nonpositive @code{tv_sec} value, @code{tv_nsec} might in the range
address@hidden@minus{}1, representing a negative nanoseconds
+offset from @code{tv_sec}.
address@hidden
 On Windows platforms (excluding Cygwin), symlinks are not supported, so
 @code{lstat} does not exist.
 @end itemize
diff --git a/doc/posix-functions/stat.texi b/doc/posix-functions/stat.texi
index d1e0f38c9..d5f73ef91 100644
--- a/doc/posix-functions/stat.texi
+++ b/doc/posix-functions/stat.texi
@@ -29,6 +29,11 @@ FreeBSD 7.2, AIX 7.1, Solaris 9, mingw64.
 On some platforms, @code{stat(".",buf)} and @code{stat("./",buf)} give
 different results:
 mingw, MSVC 14.
address@hidden
+On Solaris 11.3, when this function yields a timestamp with a
+nonpositive @code{tv_sec} value, @code{tv_nsec} might in the range
address@hidden@minus{}1, representing a negative nanoseconds
+offset from @code{tv_sec}.
 @end itemize
 
 Portability problems not fixed by Gnulib:
diff --git a/lib/fstat.c b/lib/fstat.c
index 7ab2cdcb9..3a60ebf12 100644
--- a/lib/fstat.c
+++ b/lib/fstat.c
@@ -45,6 +45,8 @@ orig_fstat (int fd, struct stat *buf)
    above.  */
 #include "sys/stat.h"
 
+#include "stat-time.h"
+
 #include <errno.h>
 #include <unistd.h>
 #ifdef WINDOWS_NATIVE
@@ -83,6 +85,6 @@ rpl_fstat (int fd, struct stat *buf)
     }
   return _gl_fstat_by_handle (h, NULL, buf);
 #else
-  return orig_fstat (fd, buf);
+  return stat_time_normalize (orig_fstat (fd, buf), buf);
 #endif
 }
diff --git a/lib/fstatat.c b/lib/fstatat.c
index 294861f51..237e68c5d 100644
--- a/lib/fstatat.c
+++ b/lib/fstatat.c
@@ -41,6 +41,8 @@ orig_fstatat (int fd, char const *filename, struct stat *buf, 
int flags)
    above.  */
 #include "sys/stat.h"
 
+#include "stat-time.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <string.h>
@@ -51,6 +53,12 @@ orig_fstatat (int fd, char const *filename, struct stat 
*buf, int flags)
 #  define LSTAT_FOLLOWS_SLASHED_SYMLINK 0
 # endif
 
+static int
+normal_fstatat (int fd, char const *file, struct stat *st, int flag)
+{
+  return stat_time_normalize (orig_fstatat (fd, file, st, flag), st);
+}
+
 /* fstatat should always follow symbolic links that end in /, but on
    Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified.
    Likewise, trailing slash on a non-directory should be an error.
@@ -63,7 +71,7 @@ orig_fstatat (int fd, char const *filename, struct stat *buf, 
int flags)
 int
 rpl_fstatat (int fd, char const *file, struct stat *st, int flag)
 {
-  int result = orig_fstatat (fd, file, st, flag);
+  int result = normal_fstatat (fd, file, st, flag);
   size_t len;
 
   if (LSTAT_FOLLOWS_SLASHED_SYMLINK || result != 0)
@@ -79,7 +87,7 @@ rpl_fstatat (int fd, char const *file, struct stat *st, int 
flag)
           errno = ENOTDIR;
           return -1;
         }
-      result = orig_fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW);
+      result = normal_fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW);
     }
   /* Fix stat behavior.  */
   if (result == 0 && !S_ISDIR (st->st_mode) && file[len - 1] == '/')
diff --git a/lib/lstat.c b/lib/lstat.c
index c721a4e64..f3c617795 100644
--- a/lib/lstat.c
+++ b/lib/lstat.c
@@ -47,6 +47,8 @@ orig_lstat (const char *filename, struct stat *buf)
    above.  */
 # include "sys/stat.h"
 
+# include "stat-time.h"
+
 # include <string.h>
 # include <errno.h>
 
@@ -66,32 +68,33 @@ orig_lstat (const char *filename, struct stat *buf)
 int
 rpl_lstat (const char *file, struct stat *sbuf)
 {
-  size_t len;
-  int lstat_result = orig_lstat (file, sbuf);
-
-  if (lstat_result != 0)
-    return lstat_result;
+  int result = orig_lstat (file, sbuf);
 
   /* This replacement file can blindly check against '/' rather than
      using the ISSLASH macro, because all platforms with '\\' either
      lack symlinks (mingw) or have working lstat (cygwin) and thus do
      not compile this file.  0 len should have already been filtered
      out above, with a failure return of ENOENT.  */
-  len = strlen (file);
-  if (file[len - 1] != '/' || S_ISDIR (sbuf->st_mode))
-    return 0;
-
-  /* At this point, a trailing slash is only permitted on
-     symlink-to-dir; but it should have found information on the
-     directory, not the symlink.  Call stat() to get info about the
-     link's referent.  Our replacement stat guarantees valid results,
-     even if the symlink is not pointing to a directory.  */
-  if (!S_ISLNK (sbuf->st_mode))
+  if (result == 0)
     {
-      errno = ENOTDIR;
-      return -1;
+      if (S_ISDIR (sbuf->st_mode) || file[strlen (file) - 1] != '/')
+        result = stat_time_normalize (result, sbuf);
+      else
+        {
+          /* At this point, a trailing slash is permitted only on
+             symlink-to-dir; but it should have found information on the
+             directory, not the symlink.  Call 'stat' to get info about the
+             link's referent.  Our replacement stat guarantees valid results,
+             even if the symlink is not pointing to a directory.  */
+          if (!S_ISLNK (sbuf->st_mode))
+            {
+              errno = ENOTDIR;
+              return -1;
+            }
+          result = stat (file, sbuf);
+        }
     }
-  return stat (file, sbuf);
+  return result;
 }
 
 #endif /* HAVE_LSTAT */
diff --git a/lib/stat-time.h b/lib/stat-time.h
index 47a3bf8f2..1cf821992 100644
--- a/lib/stat-time.h
+++ b/lib/stat-time.h
@@ -20,6 +20,10 @@
 #ifndef STAT_TIME_H
 #define STAT_TIME_H 1
 
+#include "intprops.h"
+
+#include <errno.h>
+#include <stddef.h>
 #include <sys/stat.h>
 #include <time.h>
 
@@ -202,6 +206,47 @@ get_stat_birthtime (struct stat const *st)
   return t;
 }
 
+/* If a stat-like function returned RESULT, normalize the timestamps
+   in *ST, in case this platform suffers from the Solaris 11 bug where
+   tv_nsec might be negative.  Return the adjusted RESULT, setting
+   errno to EOVERFLOW if normalization overflowed.  This function
+   is intended to be private to this .h file.  */
+_GL_STAT_TIME_INLINE int
+stat_time_normalize (int result, struct stat *st)
+{
+#if defined __sun && defined STAT_TIMESPEC
+  if (result == 0)
+    {
+      long int timespec_resolution = 1000000000;
+      short int const ts_off[] = { offsetof (struct stat, st_atim),
+                                   offsetof (struct stat, st_mtim),
+                                   offsetof (struct stat, st_ctim) };
+      int i;
+      for (i = 0; i < sizeof ts_off / sizeof *ts_off; i++)
+        {
+          struct timespec *ts = (struct timespec *) ((char *) st + ts_off[i]);
+          long int q = ts->tv_nsec / timespec_resolution;
+          long int r = ts->tv_nsec % timespec_resolution;
+          if (r < 0)
+            {
+              r += timespec_resolution;
+              q--;
+            }
+          ts->tv_nsec = r;
+          /* Overflow is possible, as Solaris 11 stat can yield
+             tv_sec == TYPE_MINIMUM (time_t) && tv_nsec == -1000000000.
+             INT_ADD_WRAPV is OK, since time_t is signed on Solaris.  */
+          if (INT_ADD_WRAPV (q, ts->tv_sec, &ts->tv_sec))
+            {
+              errno = EOVERFLOW;
+              return -1;
+            }
+        }
+    }
+#endif
+  return result;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/stat.c b/lib/stat.c
index 061496322..30ba67efc 100644
--- a/lib/stat.c
+++ b/lib/stat.c
@@ -405,19 +405,23 @@ rpl_stat (char const *name, struct stat *buf)
   }
 #else
   int result = orig_stat (name, buf);
-# if REPLACE_FUNC_STAT_FILE
-  /* Solaris 9 mistakenly succeeds when given a non-directory with a
-     trailing slash.  */
-  if (result == 0 && !S_ISDIR (buf->st_mode))
+  if (result == 0)
     {
-      size_t len = strlen (name);
-      if (ISSLASH (name[len - 1]))
+# if REPLACE_FUNC_STAT_FILE
+      /* Solaris 9 mistakenly succeeds when given a non-directory with a
+         trailing slash.  */
+      if (!S_ISDIR (buf->st_mode))
         {
-          errno = ENOTDIR;
-          return -1;
+          size_t len = strlen (name);
+          if (ISSLASH (name[len - 1]))
+            {
+              errno = ENOTDIR;
+              return -1;
+            }
         }
-    }
 # endif /* REPLACE_FUNC_STAT_FILE */
+      result = stat_time_normalize (result, buf);
+    }
   return result;
 #endif
 }
diff --git a/m4/fstat.m4 b/m4/fstat.m4
index e70e533e7..256218935 100644
--- a/m4/fstat.m4
+++ b/m4/fstat.m4
@@ -1,4 +1,4 @@
-# fstat.m4 serial 5
+# fstat.m4 serial 6
 dnl Copyright (C) 2011-2017 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -10,9 +10,10 @@ AC_DEFUN([gl_FUNC_FSTAT],
   AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
 
   case "$host_os" in
-    mingw*)
-      dnl On this platform, the original stat() returns st_atime, st_mtime,
+    mingw* | solaris*)
+      dnl On MinGW, the original stat() returns st_atime, st_mtime,
       dnl st_ctime values that are affected by the time zone.
+      dnl Solaris stat can return a negative tv_nsec.
       REPLACE_FSTAT=1
       ;;
   esac
diff --git a/m4/fstatat.m4 b/m4/fstatat.m4
index 7dba52796..767eb83db 100644
--- a/m4/fstatat.m4
+++ b/m4/fstatat.m4
@@ -1,4 +1,4 @@
-# fstatat.m4 serial 3
+# fstatat.m4 serial 4
 dnl Copyright (C) 2004-2017 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -13,7 +13,7 @@ AC_DEFUN([gl_FUNC_FSTATAT],
   AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   AC_REQUIRE([gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK])
-  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_REQUIRE([AC_CANONICAL_HOST])
   AC_CHECK_FUNCS_ONCE([fstatat])
 
   if test $ac_cv_func_fstatat = no; then
@@ -46,15 +46,20 @@ AC_DEFUN([gl_FUNC_FSTATAT],
 
     case 
$gl_cv_func_fstatat_zero_flag+$gl_cv_func_lstat_dereferences_slashed_symlink in
     *yes+*yes) ;;
-    *) REPLACE_FSTATAT=1
-       case $gl_cv_func_fstatat_zero_flag in
-       *yes)
+    *) REPLACE_FSTATAT=1 ;;
+    esac
+
+    case $host_os in
+      solaris*)
+        REPLACE_FSTATAT=1 ;;
+    esac
+
+    case $REPLACE_FSTATAT,$gl_cv_func_fstatat_zero_flag in
+      1,*yes)
          AC_DEFINE([HAVE_WORKING_FSTATAT_ZERO_FLAG], [1],
            [Define to 1 if fstatat (..., 0) works.
             For example, it does not work in AIX 7.1.])
          ;;
-       esac
-       ;;
     esac
   fi
 ])
diff --git a/m4/lstat.m4 b/m4/lstat.m4
index 0b6e5d70c..6ba18cec5 100644
--- a/m4/lstat.m4
+++ b/m4/lstat.m4
@@ -1,4 +1,4 @@
-# serial 29
+# serial 30
 
 # Copyright (C) 1997-2001, 2003-2017 Free Software Foundation, Inc.
 #
@@ -10,14 +10,15 @@ dnl From Jim Meyering.
 
 AC_DEFUN([gl_FUNC_LSTAT],
 [
+  AC_REQUIRE([AC_CANONICAL_HOST])
   AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
   dnl If lstat does not exist, the replacement <sys/stat.h> does
   dnl "#define lstat stat", and lstat.c is a no-op.
   AC_CHECK_FUNCS_ONCE([lstat])
   if test $ac_cv_func_lstat = yes; then
     AC_REQUIRE([gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK])
-    case "$gl_cv_func_lstat_dereferences_slashed_symlink" in
-      *no)
+    case $host_os,$gl_cv_func_lstat_dereferences_slashed_symlink in
+      solaris* | *no)
         REPLACE_LSTAT=1
         ;;
     esac
diff --git a/m4/stat.m4 b/m4/stat.m4
index 286fcba84..183a70ffe 100644
--- a/m4/stat.m4
+++ b/m4/stat.m4
@@ -1,4 +1,4 @@
-# serial 12
+# serial 13
 
 # Copyright (C) 2009-2017 Free Software Foundation, Inc.
 #
@@ -56,6 +56,11 @@ AC_DEFUN([gl_FUNC_STAT],
           AC_DEFINE([REPLACE_FUNC_STAT_FILE], [1], [Define to 1 if stat needs
             help when passed a file name with a trailing slash]);;
       esac
+      case $host_os in
+        dnl Solaris stat can return a negative tv_nsec.
+        solaris*)
+          REPLACE_FSTAT=1 ;;
+      esac
       ;;
   esac
 ])
diff --git a/modules/fstat b/modules/fstat
index e49894184..b557494cf 100644
--- a/modules/fstat
+++ b/modules/fstat
@@ -11,6 +11,7 @@ Depends-on:
 sys_stat
 largefile
 pathmax         [test $REPLACE_FSTAT = 1]
+stat-time       [test $REPLACE_FSTAT = 1]
 unistd          [test $REPLACE_FSTAT = 1]
 verify          [test $REPLACE_FSTAT = 1]
 msvc-nothrow    [test $REPLACE_FSTAT = 1]
diff --git a/modules/fstatat b/modules/fstatat
index 3d25cc8ec..c31184675 100644
--- a/modules/fstatat
+++ b/modules/fstatat
@@ -20,6 +20,7 @@ lstat           [test $HAVE_FSTATAT = 0 || test 
$REPLACE_FSTATAT = 1]
 openat-die      [test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1]
 openat-h        [test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1]
 save-cwd        [test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1]
+stat-time       [test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1]
 
 configure.ac:
 gl_FUNC_FSTATAT
diff --git a/modules/stat-time b/modules/stat-time
index feeb5106c..2f137fc25 100644
--- a/modules/stat-time
+++ b/modules/stat-time
@@ -8,8 +8,10 @@ m4/stat-time.m4
 
 Depends-on:
 time
+errno
 extensions
 extern-inline
+intprops
 
 configure.ac:
 gl_STAT_TIME
-- 
2.14.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]