* 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 <egg...@cs.ucla.edu> + + 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 <egg...@cs.ucla.edu> 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 +@minus{}1000000000..@minus{}1, representing a negative nanoseconds +offset from @code{tv_sec}. +@item 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. +@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 +@minus{}1000000000..@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 +@minus{}1000000000..@minus{}1, representing a negative nanoseconds +offset from @code{tv_sec}. +@item 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. +@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 +@minus{}1000000000..@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