macOS also checks struct stat's st_gen, which is easy and seems like a
good idea on platforms that have st_gen. Proposed tzcode patch attached
and installed in the GitHub repository.From 46466d1f82b9cddbb590307fdfc21b6bfcb578f5 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Sun, 5 Oct 2025 17:12:01 -0700
Subject: [PROPOSED] If detecting TZ changes, also check st_gen
This can help in unlikely NFS scenarios.
* Makefile: Mention this configuration variable.
* localtime.c (HAVE_STRUCT_STAT_ST_GEN):
Default to 1 if FreeBSD-like, 0 otherwise.
(tzfile_changed) [HAVE_STRUCT_STAT_ST_GEN]: Also check st_gen.
---
Makefile | 2 ++
localtime.c | 24 +++++++++++++++++++++++-
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 85c2c50b..f4804213 100644
--- a/Makefile
+++ b/Makefile
@@ -276,6 +276,8 @@ LDLIBS=
# -DHAVE_STRNLEN=0 if your system lacks the strnlen function+
# -DHAVE_STRTOLL=0 if your system lacks the strtoll function+
# -DHAVE_STRUCT_STAT_ST_CTIM=0 if struct stat lacks a member st_ctim+
+# -DHAVE_STRUCT_STAT_ST_GEN=1 if struct stat has a member st_gen, 0 if not
+# (default is guessed)
# -DHAVE_STRUCT_TIMESPEC=0 if your system lacks struct timespec+
# -DHAVE_SYMLINK=0 if your system lacks the symlink function
# -DHAVE_SYS_STAT_H=0 if <sys/stat.h> does not work*
diff --git a/localtime.c b/localtime.c
index 54db0ab7..9e3abbbf 100644
--- a/localtime.c
+++ b/localtime.c
@@ -44,6 +44,15 @@ struct stat { char st_ctime, st_dev, st_ino; }
# define st_ctim st_ctimespec
#endif
+#ifndef HAVE_STRUCT_STAT_ST_GEN
+# if (defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+ || (defined __APPLE__ && defined __MACH__))
+# define HAVE_STRUCT_STAT_ST_GEN 1
+# else
+# define HAVE_STRUCT_STAT_ST_GEN 0
+# endif
+#endif
+
#if defined THREAD_SAFE && THREAD_SAFE
# include <pthread.h>
static pthread_mutex_t locallock = PTHREAD_MUTEX_INITIALIZER;
@@ -628,12 +637,22 @@ tzfile_changed(int fd, struct stat *st)
static struct timespec old_ctim;
static dev_t old_dev;
static ino_t old_ino;
+#if HAVE_STRUCT_STAT_ST_GEN
+ static uint_fast64_t old_gen;
+ static_assert(sizeof st->st_gen <= sizeof old_gen);
+#endif
if (!st->st_ctime && fstat(fd, st) < 0) {
/* We do not know the file's state, so reset. */
old_ctim.tv_sec = 0;
return true;
} else {
+#if HAVE_STRUCT_STAT_ST_GEN
+ uint_fast64_t xor_gen = st->st_gen ^ old_gen;
+#else
+ int xor_gen = 0;
+#endif
+
/* Use the change time, as it changes more reliably; mod time can
be set back with futimens etc. Use subsecond timestamp
resolution if available, as this can help distinguish files on
@@ -647,10 +666,13 @@ tzfile_changed(int fd, struct stat *st)
#endif
if ((ctim.tv_sec ^ old_ctim.tv_sec) | (ctim.tv_nsec ^ old_ctim.tv_nsec)
- | (st->st_dev ^ old_dev) | (st->st_ino ^ old_ino)) {
+ | (st->st_dev ^ old_dev) | (st->st_ino ^ old_ino) | xor_gen) {
old_ctim = ctim;
old_dev = st->st_dev;
old_ino = st->st_ino;
+#if HAVE_STRUCT_STAT_ST_GEN
+ old_gen = st->st_gen;
+#endif
return true;
}
--
2.48.1