diff --git a/man/systemd-dissect.xml b/man/systemd-dissect.xml index b4ff042bb9..670fb2a574 100644 --- a/man/systemd-dissect.xml +++ b/man/systemd-dissect.xml @@ -209,7 +209,8 @@ of losetup --find --show --partscan, but will validate the image as DDI before attaching, and derive the correct sector size to use automatically. Moreover, it ensures the per-partition block devices are created before returning and attempts to enable direct IO mode, if - possible. Takes a path to a disk image file. + possible. Takes a path to a disk image file. Use to attach images that do + not qualify as DDI. If combined with output of the block device name is suppressed. @@ -541,6 +542,18 @@ + + + + If combined with , skips validation of the image and + attaches it to the loopback block device as it is, even if it does not qualify as DDI. If this + switch is used, no image dissection takes place, and thus it is not guaranteed that the + per-partition block devices are created before the command returns. May not be combined with any + other command than . + + + + diff --git a/man/systemd-loop@.service.xml b/man/systemd-loop@.service.xml index 57ed181263..61d984c6db 100644 --- a/man/systemd-loop@.service.xml +++ b/man/systemd-loop@.service.xml @@ -33,7 +33,7 @@ systemd-escape1's to properly escape a file system path. - This unit invokes systemd-dissect --attach --quiet --loop-ref-auto on the + This unit invokes systemd-dissect --attach --relax --quiet --loop-ref-auto on the specified files, see systemd-dissect1 for details. diff --git a/shell-completion/bash/systemd-dissect b/shell-completion/bash/systemd-dissect index a9fa639c3f..10366b38f1 100644 --- a/shell-completion/bash/systemd-dissect +++ b/shell-completion/bash/systemd-dissect @@ -37,7 +37,8 @@ _systemd_dissect() { -r --read-only --mkdir --rmdir - --in-memory' + --in-memory + --relax' [ARG]='-m --mount -M -u --umount -U --attach diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 29bf421819..eb113f4d00 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -109,6 +109,7 @@ static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID; static bool arg_all = false; static uid_t arg_uid_base = UID_INVALID; static bool arg_quiet = false; +static bool arg_relax = false; static ImageFilter *arg_image_filter = NULL; static int arg_copy_ownership = -1; @@ -355,6 +356,10 @@ static int parse_argv(int argc, char *argv[]) { arg_loop_ref_auto = true; break; + OPTION_LONG("relax", NULL, "Skip DDI validation, attach image as it is"): + arg_relax = true; + break; + OPTION_LONG("image-policy", "POLICY", "Specify image dissection policy"): r = parse_image_policy_argument(opts.arg, &arg_image_policy); if (r < 0) @@ -707,6 +712,10 @@ static int parse_argv(int argc, char *argv[]) { return r; } + if (arg_relax && arg_action != ACTION_ATTACH) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "The --relax switch is only supported in combination with --attach."); + SET_FLAG(arg_flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH, isatty_safe(STDIN_FILENO)); return 1; @@ -1809,19 +1818,16 @@ static int action_discover(void) { return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend); } -static int action_attach(DissectedImage *m, LoopDevice *d) { +static int action_attach(LoopDevice *d) { int r; - assert(m); assert(d); r = loop_device_set_autoclear(d, false); if (r < 0) return log_error_errno(r, "Failed to disable auto-clear logic on loopback device: %m"); - r = dissected_image_relinquish(m); - if (r < 0) - return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m"); + loop_device_relinquish(d); if (!arg_quiet) puts(d->node); @@ -1849,10 +1855,24 @@ static int action_detach(const char *path) { if (r < 0) return log_error_errno(r, "Failed to open '%s' as loopback block device: %m", path); - } else if (S_ISREG(st.st_mode)) { + /* If the specified block device is not a loopback block device itself, it might be the + * backing device of one (this is the case when systemd-loop@.service is instantiated for a + * block device, e.g. a CD-ROM drive, in which case it is passed the backing device on + * ExecStop= too). Hence, in that case search for the loopback block device backed by it + * below, the same way as we do for regular files. */ + if (LOOP_DEVICE_IS_FOREIGN(loop)) + loop = loop_device_unref(loop); + + } else if (!S_ISREG(st.st_mode)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'%s' is neither a block device nor a regular file, refusing.", path); + + if (!loop) { _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; - /* If a regular file is specified, search for a loopback block device that is backed by it */ + /* If a regular file (or a block device that is not a loopback block device) is specified, + * search for a loopback block device that is backed by it. Note that the kernel reports + * the backing inode of a loopback device both for regular files and for block device + * nodes, hence we can match by inode in both cases. */ r = sd_device_enumerator_new(&e); if (r < 0) @@ -1997,7 +2017,7 @@ static int run(int argc, char *argv[]) { ; } - if (arg_image) { + if (arg_image && !arg_relax) { r = verity_settings_load( &arg_verity_settings, arg_image, @@ -2053,19 +2073,26 @@ static int run(int argc, char *argv[]) { log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref); } - r = dissect_loop_device_and_warn( - d, - &arg_verity_settings, - /* mount_options= */ NULL, - arg_image_policy, - arg_image_filter, - arg_flags, - &m); - if (r < 0) - return r; + /* Unless --relax is specified we insist that the image qualifies as DDI: + * dissecting it validates it against the image policy and waits until the + * kernel created the per-partition block devices, so that callers may + * access them immediately after we return. With --relax we skip dissection + * entirely, and attach the image as it is. */ + if (!arg_relax) { + r = dissect_loop_device_and_warn( + d, + &arg_verity_settings, + /* mount_options= */ NULL, + arg_image_policy, + arg_image_filter, + arg_flags, + &m); + if (r < 0) + return r; + } if (arg_action == ACTION_ATTACH) - return action_attach(m, d); + return action_attach(d); r = dissected_image_load_verity_sig_partition( m, diff --git a/test/units/TEST-50-DISSECT.dissect.sh b/test/units/TEST-50-DISSECT.dissect.sh index a8908fcfef..7a1e7daa3b 100755 --- a/test/units/TEST-50-DISSECT.dissect.sh +++ b/test/units/TEST-50-DISSECT.dissect.sh @@ -1066,6 +1066,18 @@ test ! -e "/dev/disk/by-loop-ref/$name" systemd-dissect --detach "$MINIMAL_IMAGE.raw" (! systemd-dissect --detach "$MINIMAL_IMAGE.raw") +# --attach --relax also works on images that do not qualify as DDI, e.g. an entirely blank one +BLANK_IMAGE="$(mktemp /var/tmp/blankXXX.raw)" +truncate -s 16M "$BLANK_IMAGE" +(! systemd-dissect --attach "$BLANK_IMAGE") +LOOP="$(systemd-dissect --attach --relax "$BLANK_IMAGE")" +test -b "$LOOP" +systemd-dissect --detach "$LOOP" +rm -f "$BLANK_IMAGE" + +# --relax makes no sense for the other verbs +(! systemd-dissect --relax "$MINIMAL_IMAGE.raw") + # check for confext functionality mkdir -p /run/confexts/test/etc/extension-release.d echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test diff --git a/units/systemd-loop@.service b/units/systemd-loop@.service index a063dcec86..2b31c7b2b6 100644 --- a/units/systemd-loop@.service +++ b/units/systemd-loop@.service @@ -20,5 +20,5 @@ After=modprobe@loop.service Type=oneshot RemainAfterExit=yes TimeoutSec=infinity -ExecStart=systemd-dissect --attach --loop-ref-auto --quiet %f +ExecStart=systemd-dissect --attach --relax --loop-ref-auto --quiet %f ExecStop=systemd-dissect --detach %f