dissect: optionally allow attaching images that do not qualify as DDIs with --attach (#43583)

It's nice to use --attach since it validates our images. So far, it
strictly insisted on checking that the image was a DDI however, which
limits its use for certain usecases.

Add "--relax" to address this limitation.

Fixes: #43605
This commit is contained in:
Yu Watanabe
2026-09-23 08:54:29 +09:00
committed by GitHub
6 changed files with 76 additions and 23 deletions
+14 -1
View File
@@ -209,7 +209,8 @@
of <command>losetup --find --show --partscan</command>, 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.</para>
possible. Takes a path to a disk image file. Use <option>--relax</option> to attach images that do
not qualify as DDI.</para>
<para>If combined with <option>--quiet</option> output of the block device name is suppressed.</para>
@@ -541,6 +542,18 @@
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--relax</option></term>
<listitem><para>If combined with <option>--attach</option>, 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 <option>--attach</option>.</para>
<xi:include href="version-info.xml" xpointer="v263"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--mtree-hash=no</option></term>
+1 -1
View File
@@ -33,7 +33,7 @@
<citerefentry><refentrytitle>systemd-escape</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<option>--path</option> to properly escape a file system path.</para>
<para>This unit invokes <command>systemd-dissect --attach --quiet --loop-ref-auto</command> on the
<para>This unit invokes <command>systemd-dissect --attach --relax --quiet --loop-ref-auto</command> on the
specified files, see
<citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details.</para>
+2 -1
View File
@@ -37,7 +37,8 @@ _systemd_dissect() {
-r --read-only
--mkdir
--rmdir
--in-memory'
--in-memory
--relax'
[ARG]='-m --mount -M
-u --umount -U
--attach
+46 -19
View File
@@ -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,
+12
View File
@@ -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
+1 -1
View File
@@ -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