tmpfiles: do not fail --clean when a file disappears during cleanup

dir_cleanup() stores the result of xstatx_full() in r, which is also the
function's return value. If a file is removed between readdir() and
statx(), it is skipped, but when it was the last entry r stays -ENOENT
and --clean fails with EX_CANTCREAT without logging anything. A
successful statx() also overwrote errors recorded for earlier entries.

Use a separate variable for the statx() result.

Reproducer:
  d=$(mktemp -d)
  echo "d $d - - - 1d" >$d.conf
  while :; do touch $d/f{1..200}; rm -f $d/f*; done &
  for i in $(seq 200); do systemd-tmpfiles --clean $d.conf; echo $?; done | sort | uniq -c
  kill %1

Before:
    177 0
     23 73

After:
    200 0

Follow-up for a98a6eb95c.
Follow-up for 10072d8be4.

(cherry picked from commit 4c250e3f1325f91cdee047f7daeaf532abe4af6f)
This commit is contained in:
Nick
2026-10-16 17:24:51 +01:00
committed by Luca Boccassi
parent 5cfe544a1a
commit c918ac7401
+10 -10
View File
@@ -715,18 +715,18 @@ static int dir_cleanup(
continue;
struct statx sx;
r = xstatx_full(dirfd(d), de->d_name,
AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT,
/* xstatx_flags= */ 0,
STATX_TYPE|STATX_MODE|STATX_UID,
STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_BTIME,
STATX_ATTR_MOUNT_ROOT,
&sx);
if (r == -ENOENT)
int k = xstatx_full(dirfd(d), de->d_name,
AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT,
/* xstatx_flags= */ 0,
STATX_TYPE|STATX_MODE|STATX_UID,
STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_BTIME,
STATX_ATTR_MOUNT_ROOT,
&sx);
if (k == -ENOENT)
continue;
if (r < 0) {
if (k < 0) {
/* FUSE, NFS mounts, SELinux might return EACCES */
log_full_errno(r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
log_full_errno(k == -EACCES ? LOG_DEBUG : LOG_ERR, k,
"statx(%s/%s) failed: %m", p, de->d_name);
continue;
}