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

Reply via email to