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.
This commit is contained in:
Ivan Shapovalov
2026-04-06 22:53:00 +02:00
parent 9c735dec67
commit e18b75c3f1
+16 -8
View File
@@ -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 &&