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