tmpfiles: skip redundant label writes to avoid unnecessary timestamp changes

When systemd-tmpfiles processes a 'z' (relabel) entry, fd_set_perms()
unconditionally calls label_fix_full() even when mode, owner, and group
already match. This causes setfilecon_raw() (SELinux) or xsetxattr() (SMACK)
to write the security label even if it is already correct, which on some
kernels updates the file's timestamps unnecessarily.

Fix this by comparing the current label with the desired label before
writing, and skipping the write when they already match. This is consistent
with how fd_set_perms() already skips chmod/chown when the values are
unchanged.
This commit is contained in:
Nandakumar Raghavan
2026-04-08 14:48:10 +01:00
committed by Luca Boccassi
parent 40d0fb2eb2
commit 8a8f22f1d8
3 changed files with 39 additions and 27 deletions
+12
View File
@@ -301,6 +301,18 @@ int xsetxattr_full(
if (size == SIZE_MAX)
size = strlen(value);
/* Skip the write if the xattr already has the correct value, to avoid
* unnecessary timestamp changes on the file. Only do this for plain
* replace mode (xattr_flags == 0) — XATTR_CREATE callers expect
* -EEXIST when the xattr already exists. */
_cleanup_free_ char *old_value = NULL;
size_t old_size;
if (xattr_flags == 0 &&
getxattr_at_malloc(fd, path, name, at_flags, &old_value, &old_size) >= 0 &&
memcmp_nn(old_value, old_size, value, size) == 0)
return 0;
if (have_xattrat && !isempty(path)) {
struct xattr_args args = {
.value = PTR_TO_UINT64(value),
+26 -18
View File
@@ -360,6 +360,19 @@ void mac_selinux_disable_logging(void) {
}
#if HAVE_SELINUX
static int setfilecon_idempotent(int fd, const char *context) {
_cleanup_freecon_ char *oldcon = NULL;
assert(fd >= 0);
assert(context);
/* Read current context via /proc/self/fd/ so this works for O_PATH fds too */
if (sym_getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq_ptr(context, oldcon))
return 0; /* Already correct */
return RET_NERRNO(sym_setfilecon_raw(FORMAT_PROC_FD_PATH(fd), context));
}
static int selinux_fix_fd(
int fd,
const char *label_path,
@@ -389,25 +402,19 @@ static int selinux_fix_fd(
return log_selinux_enforcing_errno(errno, "Unable to lookup intended SELinux security context of %s: %m", label_path);
}
r = RET_NERRNO(sym_setfilecon_raw(FORMAT_PROC_FD_PATH(fd), fcon));
if (r < 0) {
/* If the FS doesn't support labels, then exit without warning */
if (ERRNO_IS_NOT_SUPPORTED(r))
return 0;
r = setfilecon_idempotent(fd, fcon);
if (r >= 0)
return 0;
/* It the FS is read-only and we were told to ignore failures caused by that, suppress error */
if (r == -EROFS && (flags & LABEL_IGNORE_EROFS))
return 0;
/* If the FS doesn't support labels, then exit without warning */
if (ERRNO_IS_NOT_SUPPORTED(r))
return 0;
/* If the old label is identical to the new one, suppress any kind of error */
_cleanup_freecon_ char *oldcon = NULL;
if (sym_getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq_ptr(fcon, oldcon))
return 0;
/* If the FS is read-only and we were told to ignore failures caused by that, suppress error */
if (r == -EROFS && (flags & LABEL_IGNORE_EROFS))
return 0;
return log_selinux_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", label_path);
}
return 0;
return log_selinux_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", label_path);
}
#endif
@@ -495,8 +502,9 @@ int mac_selinux_apply_fd(int fd, const char *path, const char *label) {
assert(label);
if (sym_setfilecon_raw(FORMAT_PROC_FD_PATH(fd), label) < 0)
return log_selinux_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, strna(path));
r = setfilecon_idempotent(fd, label);
if (r < 0)
return log_selinux_enforcing_errno(r, "Failed to set SELinux security context %s on path %s: %m", label, strna(path));
#endif
return 0;
}
+1 -9
View File
@@ -149,16 +149,8 @@ static int smack_fix_fd(
to ignore failures caused by that,
suppress error */
return 0;
if (r < 0) {
/* If the old label is identical to the new one, suppress any kind of error */
_cleanup_free_ char *old_label = NULL;
if (fgetxattr_malloc(fd, "security.SMACK64", &old_label, /* ret_size= */ NULL) >= 0 &&
streq(old_label, label))
return 0;
if (r < 0)
return log_debug_errno(r, "Unable to fix SMACK label of '%s': %m", label_path);
}
return 0;
}