From d07aa23c872d13baa2816a69326eea6f9d5271a0 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Thu, 12 Feb 2026 21:57:26 +0000 Subject: [PATCH 1/2] portable: fix --force flag combination with directory extension The check for image type uses the wrong variable, so it's applied when it shouldn't. Follow-up for 06768b90a32ac0d36252ebc5f426ad471bf29fce --- src/portable/portable.c | 2 +- test/units/TEST-29-PORTABLE.directory.sh | 13 +++++++++++++ test/units/TEST-29-PORTABLE.image.sh | 4 ++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/portable/portable.c b/src/portable/portable.c index 2b709f22f5f..5341fb60b67 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -1465,7 +1465,7 @@ static int install_chroot_dropin( ext->path, /* With --force tell PID1 to avoid enforcing that the image and * extension-release. have to match. */ - !IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) && + !IN_SET(ext->type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) && FLAGS_SET(flags, PORTABLE_FORCE_EXTENSION) ? ":x-systemd.relax-extension-release-check\n" : "\n", diff --git a/test/units/TEST-29-PORTABLE.directory.sh b/test/units/TEST-29-PORTABLE.directory.sh index 81aae08a3d6..8f35c9ee083 100755 --- a/test/units/TEST-29-PORTABLE.directory.sh +++ b/test/units/TEST-29-PORTABLE.directory.sh @@ -127,6 +127,19 @@ test -L /run/systemd/system.attached/app0.service.d/10-profile.conf test -L /run/systemd/system.attached/app1.service.d/10-profile.conf portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 +# Ensure that --force works with directory extensions, and that ExtensionDirectories= +# is not decorated with :x-systemd.relax-extension-release-check +portablectl "${ARGS[@]}" attach --force --copy=symlink --now --runtime --extension /tmp/app0 /tmp/rootdir app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension app0 rootdir)" +[[ "${status}" == "running-runtime" ]] + +grep -q -F "ExtensionDirectories=" /run/systemd/system.attached/app0.service.d/20-portable.conf +(! grep -q -F "x-systemd.relax-extension-release-check" /run/systemd/system.attached/app0.service.d/20-portable.conf) + +portablectl detach --now --runtime --extension /tmp/app0 /tmp/rootdir app0 + # Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce. # Provides coverage for https://github.com/systemd/systemd/issues/23481 portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0 diff --git a/test/units/TEST-29-PORTABLE.image.sh b/test/units/TEST-29-PORTABLE.image.sh index 8b930d40f2c..cc83ebc5d63 100755 --- a/test/units/TEST-29-PORTABLE.image.sh +++ b/test/units/TEST-29-PORTABLE.image.sh @@ -189,6 +189,10 @@ systemctl is-active app0.service status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)" [[ "${status}" == "running-runtime" ]] +# Ensure --force adds relax-extension-release-check for image extensions +grep -q -F "ExtensionImages=" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "ExtensionImagePolicy=" /run/systemd/system.attached/app0.service.d/20-portable.conf + portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -F "Extension Release: /tmp/app10.raw" >/dev/null # Ensure that we can detach even when an image has been deleted already (stop the unit manually as From 03f6a8b9d9ff863e8e35176aee47a1ff538c76d6 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Thu, 12 Feb 2026 21:59:29 +0000 Subject: [PATCH 2/2] portable: do not apply extension image policy if not attaching image Image policy is only for images, so skip for other types Follow-up for d05961549277f15399a45cdf42d4d5f3e5ed8097 --- src/portable/portable.c | 2 +- test/units/TEST-29-PORTABLE.image.sh | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/portable/portable.c b/src/portable/portable.c index 5341fb60b67..403a05005ce 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -1475,7 +1475,7 @@ static int install_chroot_dropin( "LogExtraFields=PORTABLE_EXTENSION=", extension_base_name, "\n")) return -ENOMEM; - if (pinned_ext_image_policy) { + if (pinned_ext_image_policy && !IN_SET(ext->type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME)) { _cleanup_free_ char *policy_str = NULL; r = image_policy_to_string(pinned_ext_image_policy, /* simplify= */ true, &policy_str); diff --git a/test/units/TEST-29-PORTABLE.image.sh b/test/units/TEST-29-PORTABLE.image.sh index cc83ebc5d63..36d187a7528 100755 --- a/test/units/TEST-29-PORTABLE.image.sh +++ b/test/units/TEST-29-PORTABLE.image.sh @@ -249,3 +249,19 @@ status="$(portablectl is-attached --extension app1 minimal_0)" [[ "${status}" == "attached-runtime" ]] portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app + +# Ensure that when mixing directory and image extensions, ExtensionImagePolicy= is only +# applied to image extensions and not to directory extensions +mkdir -p /tmp/app1 +mount /tmp/app1.raw /tmp/app1 +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app1 --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service + +grep -q -F "ExtensionDirectories=/tmp/app1" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "ExtensionImages=/tmp/app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf +# ExtensionImagePolicy= should appear exactly once (for the image, not the directory) +[[ "$(grep -c -F "ExtensionImagePolicy=" /run/systemd/system.attached/app0.service.d/20-portable.conf)" == "1" ]] + +portablectl detach --now --runtime --extension /tmp/app1 --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 +umount -l /tmp/app1