From 11af802af31b69e8e478baae3ea6e5b5090bafaf Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Tue, 21 Apr 2026 20:10:01 -0500 Subject: feat: PrivateTmp=yes drop-in for systemd-tmpfiles on ZFS-root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On ZFS-on-root, statx() across sibling services' /var/tmp/systemd-private-*/tmp mounts returns errno 132 (ENOTNAM). This produces 10-30 journal errors per boot and causes systemd-tmpfiles-clean.service to fail every periodic run (exit 73 / CANTCREAT). Running tmpfiles inside its own mount namespace avoids traversing sibling private-tmp paths. install_zfs() now calls configure_tmpfiles_private_tmp() between configure_zfs_tools and sync_efi_partitions, so the genesis snapshot captures the drop-ins. Btrfs path is untouched — errno 132 is ZFS-specific. The drop-in file-writing is factored into install_dropin() in lib/common.sh (service, name, root; body from stdin). Six bats tests exercise path, content, directory permissions, idempotent overwrite, empty content, and special-character preservation. Full root-cause write-up and verification steps in docs/zfs-tmpfiles-private-tmp-fix.md. --- tests/unit/test_common.bats | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'tests/unit') diff --git a/tests/unit/test_common.bats b/tests/unit/test_common.bats index 04f4e09..c81d2e3 100644 --- a/tests/unit/test_common.bats +++ b/tests/unit/test_common.bats @@ -178,3 +178,71 @@ setup() { prompt_password PASS "label" 0 < <(printf '\n\n') >/dev/null [ -z "$PASS" ] } + +############################# +# install_dropin +############################# + +setup_dropin_tmp() { + DROPIN_ROOT=$(mktemp -d) +} + +teardown_dropin_tmp() { + [ -n "${DROPIN_ROOT:-}" ] && rm -rf "$DROPIN_ROOT" +} + +@test "install_dropin writes conf file at expected path" { + setup_dropin_tmp + install_dropin foo bar "$DROPIN_ROOT" <<< "[Service]" + [ -f "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf" ] + teardown_dropin_tmp +} + +@test "install_dropin writes stdin content verbatim" { + setup_dropin_tmp + install_dropin foo bar "$DROPIN_ROOT" <<< "[Service] +PrivateTmp=yes" + run cat "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf" + [ "$status" -eq 0 ] + [[ "$output" == *"[Service]"* ]] + [[ "$output" == *"PrivateTmp=yes"* ]] + teardown_dropin_tmp +} + +@test "install_dropin creates dropin dir with 755 perms" { + setup_dropin_tmp + install_dropin foo bar "$DROPIN_ROOT" <<< "x" + local perms + perms=$(stat -c '%a' "$DROPIN_ROOT/etc/systemd/system/foo.service.d") + [ "$perms" = "755" ] + teardown_dropin_tmp +} + +@test "install_dropin is idempotent — second call overwrites content" { + setup_dropin_tmp + install_dropin foo bar "$DROPIN_ROOT" <<< "first" + install_dropin foo bar "$DROPIN_ROOT" <<< "second" + run cat "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf" + [ "$output" = "second" ] + teardown_dropin_tmp +} + +@test "install_dropin accepts empty content" { + setup_dropin_tmp + install_dropin foo bar "$DROPIN_ROOT" < /dev/null + [ -f "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf" ] + [ ! -s "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf" ] + teardown_dropin_tmp +} + +@test "install_dropin preserves special characters in content" { + setup_dropin_tmp + install_dropin foo bar "$DROPIN_ROOT" <<< '# comment with $var and `backtick` +[Service] +Environment="FOO=bar baz"' + run cat "$DROPIN_ROOT/etc/systemd/system/foo.service.d/bar.conf" + [[ "$output" == *'$var'* ]] + [[ "$output" == *'`backtick`'* ]] + [[ "$output" == *'"FOO=bar baz"'* ]] + teardown_dropin_tmp +} -- cgit v1.2.3