From e18b75c3f14960cc2a9e8bce7cce3bc3ed8c6114 Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Fri, 20 Mar 2026 16:36:44 +0100 Subject: [PATCH] tmpfiles: use `NSEC_INFINITY` consistently in dir_cleanup() Correctness analysis ==================== The *time_nsec variables are used for a total of 2 or 3 times: - twice in needs_cleanup() (lines 788, 839) - once in a recursive dir_cleanup() (line 764) as self_*time_nsec In needs_cleanup(), all passed timestamps are guarded against NSEC_INFINITY (this does not fix any real bugs as a 0 value is also older than any cutoff point and thus would not cause any deletions). Recursively in dir_cleanup(), the self_* variables are used to reset the toplevel directory utimes, where they are superficially compared against NSEC_INFINITY as a guard, but subsequently mishandled in the case when only one of the times is NSEC_INFINITY: in this case, it will be a) logged as a bogus value and b) passed through directly to timespec_store_nsec(), which does special-case it, but in a way that is invalid for futimens(). This is further fixed up by explicitly mapping NSEC_INFINITY to TIMESPEC_OMIT. This constitutes a bugfix in theory, as a ~STATX_ATIME return from statx() would have previously caused the corresponding utime to be reset to 0 epoch) rather than being omitted from being set. However, in a directory with ~STATX_ATIME, attempts to set atime would likely be ignored as well. Mostly this is a self-consistency fix that establishes that dir_cleanup() should be called with NSEC_INFINITY in place of absent timestamps. --- src/tmpfiles/tmpfiles.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 17f263790e..2b1c612f5c 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -710,10 +710,10 @@ static int dir_cleanup( continue; } - atime_nsec = FLAGS_SET(sx.stx_mask, STATX_ATIME) ? statx_timestamp_load_nsec(&sx.stx_atime) : 0; - mtime_nsec = FLAGS_SET(sx.stx_mask, STATX_MTIME) ? statx_timestamp_load_nsec(&sx.stx_mtime) : 0; - ctime_nsec = FLAGS_SET(sx.stx_mask, STATX_CTIME) ? statx_timestamp_load_nsec(&sx.stx_ctime) : 0; - btime_nsec = FLAGS_SET(sx.stx_mask, STATX_BTIME) ? statx_timestamp_load_nsec(&sx.stx_btime) : 0; + atime_nsec = FLAGS_SET(sx.stx_mask, STATX_ATIME) ? statx_timestamp_load_nsec(&sx.stx_atime) : NSEC_INFINITY; + mtime_nsec = FLAGS_SET(sx.stx_mask, STATX_MTIME) ? statx_timestamp_load_nsec(&sx.stx_mtime) : NSEC_INFINITY; + ctime_nsec = FLAGS_SET(sx.stx_mask, STATX_CTIME) ? statx_timestamp_load_nsec(&sx.stx_ctime) : NSEC_INFINITY; + btime_nsec = FLAGS_SET(sx.stx_mask, STATX_BTIME) ? statx_timestamp_load_nsec(&sx.stx_btime) : NSEC_INFINITY; sub_path = path_join(p, de->d_name); if (!sub_path) { @@ -867,11 +867,19 @@ finish: log_action("Would restore", "Restoring", "%s access and modification time on \"%s\": %s, %s", p, - FORMAT_TIMESTAMP_STYLE(self_atime_nsec / NSEC_PER_USEC, TIMESTAMP_US), - FORMAT_TIMESTAMP_STYLE(self_mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US)); + self_atime_nsec != NSEC_INFINITY + ? FORMAT_TIMESTAMP_STYLE(self_atime_nsec / NSEC_PER_USEC, TIMESTAMP_US) + : "(omitted)", + self_mtime_nsec != NSEC_INFINITY + ? FORMAT_TIMESTAMP_STYLE(self_mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US) + : "(omitted)"); - timespec_store_nsec(ts + 0, self_atime_nsec); - timespec_store_nsec(ts + 1, self_mtime_nsec); + ts[0] = self_atime_nsec != NSEC_INFINITY + ? *TIMESPEC_STORE_NSEC(self_atime_nsec) + : TIMESPEC_OMIT; + ts[1] = self_mtime_nsec != NSEC_INFINITY + ? *TIMESPEC_STORE_NSEC(self_mtime_nsec) + : TIMESPEC_OMIT; /* Restore original directory timestamps */ if (!arg_dry_run &&