tmpfiles: do not require STATX_ATIME (#41232)

Timestamps are not guaranteed to be set by `statx()`, and their presence
should not be asserted as a proxy to judge the kernel version. In
particular, `STATX_ATIME` is omitted from the return when querying a
file on a `noatime` superblock, causing spurious errors from tmpfiles:

```console
# SYSTEMD_LOG_LEVEL=debug systemd-tmpfiles --clean
<...>
Running clean action for entry X /var/tmp/systemd-private-94cc8a77688e497f96d5b9019e66ed6f-*/tmp
statx() does not support 'STATX_ATIME' mask (running on an old kernel?)
statx(/var/tmp/systemd-private-94cc8a77688e497f96d5b9019e66ed6f-prometheus-smartctl-exporter.service-GKguQK/tmp) failed: Protocol driver not attached
statx() does not support 'STATX_ATIME' mask (running on an old kernel?)
statx(/var/tmp/systemd-private-94cc8a77688e497f96d5b9019e66ed6f-systemd-logind.service-k8j52T/tmp) failed: Protocol driver not attached
statx() does not support 'STATX_ATIME' mask (running on an old kernel?)
statx(/var/tmp/systemd-private-94cc8a77688e497f96d5b9019e66ed6f-irqbalance.service-7RJkev/tmp) failed: Protocol driver not attached
statx() does not support 'STATX_ATIME' mask (running on an old kernel?)
statx(/var/tmp/systemd-private-94cc8a77688e497f96d5b9019e66ed6f-chronyd.service-8hkO5G/tmp) failed: Protocol driver not attached
statx() does not support 'STATX_ATIME' mask (running on an old kernel?)
statx(/var/tmp/systemd-private-94cc8a77688e497f96d5b9019e66ed6f-dbus-broker.service-6P6LVl/tmp) failed: Protocol driver not attached
statx() does not support 'STATX_ATIME' mask (running on an old kernel?)
statx(/var/tmp/systemd-private-94cc8a77688e497f96d5b9019e66ed6f-nginx.service-B5HX8B/tmp) failed: Protocol driver not attached
Running clean action for entry x /var/tmp/systemd-private-94cc8a77688e497f96d5b9019e66ed6f-*
Running clean action for entry q /var/tmp
statx() does not support 'STATX_ATIME' mask (running on an old kernel?)
statx(/var/tmp) failed: Protocol driver not attached
<...>
```

Additionally, refactor `dir_cleanup()` slightly for self-consistency to
make
it evident that the `NSEC_INFINITY` transformation is correct.

Fixes #41227.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-04-08 13:56:13 +02:00
committed by GitHub
2 changed files with 25 additions and 12 deletions
+1
View File
@@ -114,6 +114,7 @@ struct timespec* timespec_store(struct timespec *ts, usec_t u);
struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n);
#define TIMESPEC_STORE(u) timespec_store(&(struct timespec) {}, (u))
#define TIMESPEC_STORE_NSEC(n) timespec_store_nsec(&(struct timespec) {}, (n))
usec_t timeval_load(const struct timeval *tv) _pure_;
struct timeval* timeval_store(struct timeval *tv, usec_t u);
+24 -12
View File
@@ -592,8 +592,8 @@ static int opendir_and_stat(
/* path= */ NULL,
AT_EMPTY_PATH,
/* xstatx_flags= */ 0,
STATX_MODE|STATX_INO|STATX_ATIME|STATX_MTIME,
/* optional_mask= */ 0,
STATX_MODE|STATX_INO,
STATX_ATIME|STATX_MTIME,
STATX_ATTR_MOUNT_ROOT,
&sx);
if (r < 0)
@@ -711,10 +711,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) {
@@ -868,11 +868,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 &&
@@ -3117,6 +3125,7 @@ static int clean_item_instance(
return 0;
usec_t cutoff = n - i->age;
nsec_t atime_nsec, mtime_nsec;
_cleanup_closedir_ DIR *d = NULL;
struct statx sx;
@@ -3127,6 +3136,9 @@ static int clean_item_instance(
if (r <= 0)
return r;
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;
if (DEBUG_LOGGING) {
_cleanup_free_ char *ab_f = NULL, *ab_d = NULL;
@@ -3146,8 +3158,8 @@ static int clean_item_instance(
}
return dir_cleanup(c, i, instance, d,
statx_timestamp_load_nsec(&sx.stx_atime),
statx_timestamp_load_nsec(&sx.stx_mtime),
atime_nsec,
mtime_nsec,
cutoff * NSEC_PER_USEC,
sx.stx_dev_major, sx.stx_dev_minor,
mountpoint,