From: Daniel Kahn Gillmor Date: Fri, 22 Feb 2019 16:05:38 -0500 Subject: zonefile: Verify mtime against full-precision timestamp We've just used 1-second granularity mtime to check if a file has changed. But if two updates happen within a calendar second, and knotd notices the first one and reloads the file, it might never notice the second change and continue serving the old file. We can see this happening in intermittent test suite failures in the debian continuous integration servers: https://ci.debian.net/packages/k/knot/unstable/amd64 Using nanosecond-granularity timestamps should make these problems go away. Signed-off-by: Daniel Kahn Gillmor --- src/knot/events/handlers/load.c | 6 ++++-- src/knot/zone/zone.c | 2 +- src/knot/zone/zone.h | 2 +- src/knot/zone/zonedb-load.c | 6 ++++-- src/knot/zone/zonefile.c | 4 ++-- src/knot/zone/zonefile.h | 2 +- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/knot/events/handlers/load.c b/src/knot/events/handlers/load.c index 7410d30..1f8f368 100644 --- a/src/knot/events/handlers/load.c +++ b/src/knot/events/handlers/load.c @@ -73,10 +73,12 @@ int event_load(conf_t *conf, zone_t *zone) // If configured, attempt to load zonefile. if (zf_from != ZONEFILE_LOAD_NONE) { - time_t mtime; + struct timespec mtime; char *filename = conf_zonefile(conf, zone->name); ret = zonefile_exists(filename, &mtime); - bool zonefile_unchanged = (zone->zonefile.exists && zone->zonefile.mtime == mtime); + bool zonefile_unchanged = (zone->zonefile.exists && + zone->zonefile.mtime.tv_sec == mtime.tv_sec && + zone->zonefile.mtime.tv_nsec == mtime.tv_nsec); free(filename); if (ret == KNOT_EOK) { ret = zone_load_contents(conf, zone->name, &zf_conts); diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c index efc0caa..0ec29f1 100644 --- a/src/knot/zone/zone.c +++ b/src/knot/zone/zone.c @@ -145,7 +145,7 @@ static int flush_journal(conf_t *conf, zone_t *zone, bool allow_empty_zone) /* Update zone file attributes. */ zone->zonefile.exists = true; - zone->zonefile.mtime = st.st_mtime; + zone->zonefile.mtime = st.st_mtim; zone->zonefile.serial = serial_to; zone->zonefile.resigned = false; diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h index 360e222..09c92cc 100644 --- a/src/knot/zone/zone.h +++ b/src/knot/zone/zone.h @@ -50,7 +50,7 @@ typedef struct zone /*! \brief Zonefile parameters. */ struct { - time_t mtime; + struct timespec mtime; uint32_t serial; bool exists; bool resigned; diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c index a6e9834..f23b4b1 100644 --- a/src/knot/zone/zonedb-load.c +++ b/src/knot/zone/zonedb-load.c @@ -35,12 +35,14 @@ static bool zone_file_updated(conf_t *conf, const zone_t *old_zone, assert(zone_name); char *zonefile = conf_zonefile(conf, zone_name); - time_t mtime; + struct timespec mtime; int ret = zonefile_exists(zonefile, &mtime); free(zonefile); return (ret == KNOT_EOK && old_zone != NULL && - !(old_zone->zonefile.exists && old_zone->zonefile.mtime == mtime)); + !(old_zone->zonefile.exists && + old_zone->zonefile.mtime.tv_sec == mtime.tv_sec && + old_zone->zonefile.mtime.tv_nsec == mtime.tv_nsec)); } static zone_t *create_zone_from(const knot_dname_t *name, server_t *server) diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c index 37fc90b..0e02d21 100644 --- a/src/knot/zone/zonefile.c +++ b/src/knot/zone/zonefile.c @@ -248,7 +248,7 @@ fail: return NULL; } -int zonefile_exists(const char *path, time_t *mtime) +int zonefile_exists(const char *path, struct timespec *mtime) { if (path == NULL) { return KNOT_EINVAL; @@ -260,7 +260,7 @@ int zonefile_exists(const char *path, time_t *mtime) } if (mtime != NULL) { - *mtime = zonefile_st.st_mtime; + *mtime = zonefile_st.st_mtim; } return KNOT_EOK; diff --git a/src/knot/zone/zonefile.h b/src/knot/zone/zonefile.h index 90283ee..9d0542e 100644 --- a/src/knot/zone/zonefile.h +++ b/src/knot/zone/zonefile.h @@ -79,7 +79,7 @@ zone_contents_t *zonefile_load(zloader_t *loader); * * \return KNOT_E* */ -int zonefile_exists(const char *path, time_t *mtime); +int zonefile_exists(const char *path, struct timespec *mtime); /*! * \brief Write zone contents to zone file.