]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Support ExtensionImages=/MountImages= in user services via mountfsd
authorLuca Boccassi <luca.boccassi@gmail.com>
Wed, 15 Oct 2025 17:49:16 +0000 (18:49 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 16 Oct 2025 11:58:59 +0000 (12:58 +0100)
Support for RootImage= was added by 046a1487db00ca1a98b8cc3f5bcecb8b1f1a214b
but it was not wired in for ExtensionImages=/MountImages=

src/core/namespace.c
src/shared/dissect-image.c
src/shared/dissect-image.h
src/shared/mount-util.c

index 16159fdf84d6190f8d4d9ca8d292e651ac0847a2..00d760f7ff453c478c29d89f28b98e20a310b4a4 100644 (file)
@@ -1617,7 +1617,8 @@ static int mount_mqueuefs(const MountEntry *m) {
 static int mount_image(
                 MountEntry *m,
                 const char *root_directory,
-                const ImagePolicy *image_policy) {
+                const ImagePolicy *image_policy,
+                RuntimeScope runtime_scope) {
 
         _cleanup_(extension_release_data_done) ExtensionReleaseData rdata = {};
         ImageClass required_class = _IMAGE_CLASS_INVALID;
@@ -1652,6 +1653,7 @@ static int mount_image(
                         &rdata,
                         required_class,
                         &m->verity,
+                        runtime_scope,
                         /* ret_image= */ NULL);
         if (r == -ENOENT && m->ignore)
                 return 0;
@@ -2038,10 +2040,10 @@ static int apply_one_mount(
                 return mount_mqueuefs(m);
 
         case MOUNT_IMAGE:
-                return mount_image(m, NULL, p->mount_image_policy);
+                return mount_image(m, NULL, p->mount_image_policy, p->runtime_scope);
 
         case MOUNT_EXTENSION_IMAGE:
-                return mount_image(m, root_directory, p->extension_image_policy);
+                return mount_image(m, root_directory, p->extension_image_policy, p->runtime_scope);
 
         case MOUNT_OVERLAY:
                 return mount_overlay(m);
index 4554848e927261c60d9e7b5208feea77d2479f69..de9475e6d3282423a10d760749448914c483f55e 100644 (file)
@@ -60,6 +60,7 @@
 #include "proc-cmdline.h"
 #include "process-util.h"
 #include "resize-fs.h"
+#include "runtime-scope.h"
 #include "signal-util.h"
 #include "siphash24.h"
 #include "stat-util.h"
@@ -4416,11 +4417,13 @@ int verity_dissect_and_mount(
                 const ExtensionReleaseData *extension_release_data,
                 ImageClass required_class,
                 VeritySettings *verity,
+                RuntimeScope runtime_scope,
                 DissectedImage **ret_image) {
 
         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
         _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
         _cleanup_(verity_settings_done) VeritySettings local_verity = VERITY_SETTINGS_DEFAULT;
+        _cleanup_close_ int userns_fd = -EBADF;
         DissectImageFlags dissect_image_flags;
         bool relax_extension_release_check;
         int r;
@@ -4451,55 +4454,70 @@ int verity_dissect_and_mount(
                 DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
                 DISSECT_IMAGE_VERITY_SHARE;
 
-        /* Note that we don't use loop_device_make here, as the FD is most likely O_PATH which would not be
-         * accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */
-        r = loop_device_make_by_path(
-                        src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
-                        /* open_flags= */ -1,
-                        /* sector_size= */ UINT32_MAX,
-                        verity->data_path ? 0 : LO_FLAGS_PARTSCAN,
-                        LOCK_SH,
-                        &loop_device);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to create loop device for image: %m");
+        if (runtime_scope == RUNTIME_SCOPE_SYSTEM) {
+                /* Note that we don't use loop_device_make here, as the FD is most likely O_PATH which would not be
+                * accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */
+                r = loop_device_make_by_path(
+                                src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
+                                /* open_flags= */ -1,
+                                /* sector_size= */ UINT32_MAX,
+                                verity->data_path ? 0 : LO_FLAGS_PARTSCAN,
+                                LOCK_SH,
+                                &loop_device);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to create loop device for image: %m");
 
-        r = dissect_loop_device(
-                        loop_device,
-                        verity,
-                        options,
-                        image_policy,
-                        image_filter,
-                        dissect_image_flags,
-                        &dissected_image);
-        /* No partition table? Might be a single-filesystem image, try again */
-        if (!verity->data_path && r == -ENOPKG)
-                 r = dissect_loop_device(
+                r = dissect_loop_device(
                                 loop_device,
                                 verity,
                                 options,
                                 image_policy,
                                 image_filter,
-                                dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE,
+                                dissect_image_flags,
                                 &dissected_image);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to dissect image: %m");
+                /* No partition table? Might be a single-filesystem image, try again */
+                if (!verity->data_path && r == -ENOPKG)
+                        r = dissect_loop_device(
+                                        loop_device,
+                                        verity,
+                                        options,
+                                        image_policy,
+                                        image_filter,
+                                        dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE,
+                                        &dissected_image);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to dissect image: %m");
 
-        r = dissected_image_load_verity_sig_partition(dissected_image, loop_device->fd, verity);
-        if (r < 0)
-                return r;
+                r = dissected_image_load_verity_sig_partition(dissected_image, loop_device->fd, verity);
+                if (r < 0)
+                        return r;
 
-        r = dissected_image_guess_verity_roothash(dissected_image, verity);
-        if (r < 0)
-                return r;
+                r = dissected_image_guess_verity_roothash(dissected_image, verity);
+                if (r < 0)
+                        return r;
 
-        r = dissected_image_decrypt(
-                        dissected_image,
-                        NULL,
-                        verity,
-                        image_policy,
-                        dissect_image_flags);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to decrypt dissected image: %m");
+                r = dissected_image_decrypt(
+                                dissected_image,
+                                NULL,
+                                verity,
+                                image_policy,
+                                dissect_image_flags);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to decrypt dissected image: %m");
+        } else {
+                userns_fd = namespace_open_by_type(NAMESPACE_USER);
+                if (userns_fd < 0)
+                        return log_debug_errno(userns_fd, "Failed to open our own user namespace: %m");
+
+                r = mountfsd_mount_image(
+                                src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
+                                userns_fd,
+                                image_policy,
+                                dissect_image_flags,
+                                &dissected_image);
+                if (r < 0)
+                        return r;
+        }
 
         if (dest) {
                 r = mkdir_p_label(dest, 0755);
@@ -4515,14 +4533,16 @@ int verity_dissect_and_mount(
                         dest,
                         /* uid_shift= */ UID_INVALID,
                         /* uid_range= */ UID_INVALID,
-                        /* userns_fd= */ -EBADF,
+                        userns_fd,
                         dissect_image_flags);
         if (r < 0)
                 return log_debug_errno(r, "Failed to mount image: %m");
 
-        r = loop_device_flock(loop_device, LOCK_UN);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to unlock loopback device: %m");
+        if (loop_device) {
+                r = loop_device_flock(loop_device, LOCK_UN);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to unlock loopback device: %m");
+        }
 
         /* If we got os-release values from the caller, then we need to match them with the image's
          * extension-release.d/ content. Return -EINVAL if there's any mismatch.
index cd87ff769ac5551a099364433df17ea5721243af..a050f1070148e11a45b1c3a14fe4b766fea4d315 100644 (file)
@@ -238,7 +238,7 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
 
 int mount_image_privately_interactively(const char *path, const ImagePolicy *image_policy, DissectImageFlags flags, char **ret_directory, int *ret_dir_fd, LoopDevice **ret_loop_device);
 
-int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const ImageFilter *image_filter, const ExtensionReleaseData *required_release_data, ImageClass required_class, VeritySettings *verity, DissectedImage **ret_image);
+int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const ImageFilter *image_filter, const ExtensionReleaseData *required_release_data, ImageClass required_class, VeritySettings *verity, RuntimeScope runtime_scope, DissectedImage **ret_image);
 
 int dissect_fstype_ok(const char *fstype);
 
index d4b86e3fe94108f47537011c8b0afe5d06c5075f..c6d1373caf1ec9043afda80696e898eff9bc99dc 100644 (file)
@@ -27,6 +27,7 @@
 #include "path-util.h"
 #include "pidref.h"
 #include "process-util.h"
+#include "runtime-scope.h"
 #include "set.h"
 #include "sort-util.h"
 #include "stat-util.h"
@@ -1005,6 +1006,7 @@ static int mount_in_namespace_legacy(
                                 /* extension_release_data= */ NULL,
                                 /* required_class= */ _IMAGE_CLASS_INVALID,
                                 /* verity= */ NULL,
+                                RUNTIME_SCOPE_SYSTEM,
                                 /* ret_image= */ NULL);
         else
                 r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, NULL, MS_BIND, NULL);
@@ -1227,6 +1229,7 @@ static int mount_in_namespace(
                                 /* extension_release_data= */ NULL,
                                 /* required_class= */ _IMAGE_CLASS_INVALID,
                                 /* verity= */ NULL,
+                                RUNTIME_SCOPE_SYSTEM,
                                 &img);
                 if (r < 0)
                         return log_debug_errno(r,