From 06e4fe54a9964140bd05bbc164c676c194b9d525 Mon Sep 17 00:00:00 2001 From: Ciaran Anscomb Date: Fri, 7 Jul 2023 11:05:09 +0100 Subject: [PATCH] sanitise results of stat()/lstat() on Solaris Solaris 11 (possibly only 11.4) can return negative values for st_atim.tv_nsec, st_mtim.tv_nsec and st_ctim.tv_nsec. Gnulib already identifies this as an issue and works around it with stat_time_normalize(). I've adapted that function for this patch, omitting st_ctim (unused by rsync) and using only a very simple test for time_t overflow. The feature test macros used mean this code path will be used on _all_ Solaris builds, but this shouldn't be an issue; it would probably be safe on anything that provides tv_nsec. --- syscall.c | 18 ++++++++++++++---- util2.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/syscall.c b/syscall.c index d92074aaa..421280d1f 100644 --- a/syscall.c +++ b/syscall.c @@ -357,9 +357,14 @@ int do_mkstemp(char *template, mode_t perms) int do_stat(const char *path, STRUCT_STAT *st) { #ifdef USE_STAT64_FUNCS - return stat64(path, st); + int r = stat64(path, st); #else - return stat(path, st); + int r = stat(path, st); +#endif +#if defined __sun && defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + return stat_time_normalize(r, st); +#else + return r; #endif } @@ -367,9 +372,14 @@ int do_lstat(const char *path, STRUCT_STAT *st) { #ifdef SUPPORT_LINKS # ifdef USE_STAT64_FUNCS - return lstat64(path, st); + int r = lstat64(path, st); +# else + int r = lstat(path, st); +# endif +# if defined __sun && defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + return stat_time_normalize(r, st); # else - return lstat(path, st); + return r; # endif #else return do_stat(path, st); diff --git a/util2.c b/util2.c index b59bff0a0..87205eb42 100644 --- a/util2.c +++ b/util2.c @@ -143,3 +143,44 @@ const char *src_file(const char *file) return file + prefix; return file; } + +/* 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. + + This is adapted from Gnulib. */ + +int stat_time_normalize(int result, STRUCT_STAT *st) +{ +#if defined __sun && defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + if (result == 0) { + long int timespec_hz = 1000000000; + short int const ts_off[] = { offsetof (struct stat, st_atim), + offsetof (struct stat, st_mtim) }; + unsigned 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_hz; + long int r = ts->tv_nsec % timespec_hz; + if (r < 0) { + r += timespec_hz; + q--; + } + ts->tv_nsec = r; + /* Overflow is possible, as Solaris 11 stat can yield + tv_sec == TYPE_MINIMUM (time_t) && tv_nsec == -1000000000. */ + time_t sec = ts->tv_sec + q; + if ((q > 0 && sec < ts->tv_sec) || (q < 0 && sec > ts->tv_sec)) { + errno = EOVERFLOW; + return -1; + } + ts->tv_sec = sec; + } + } +#else + (void)st; +#endif + return result; +}