Support booting from rootfs acquired via HTTP (#36314)
This extends systemd-import-generator to not only download a disk image at boot, but also attach it to a loopback device, so that we can boot from it. We have most of the pieces already in place, this just polishes some things, to make this round. The topmost commit contains example command lines that just work to make `systemd-vmspawn` boot from a `mkosi serve` call. Note that this does not address how to get the UKI running on the target system, this only deals with the later boot phase once the UKI is already running. This is WIP, because it lacks docs, and I want to do some more polishing. But it works great. Ultimate goal, provide a complete solution so that we also can do uefi http boot for ukis
This commit is contained in:
@@ -179,9 +179,6 @@ Features:
|
||||
Net result: people who try to debug why their process gets killed should have
|
||||
some minimal, nice metadata directly on the signal event.
|
||||
|
||||
* import-generator: add option to download into /run/ rather than /var/, and
|
||||
make it default in the initrd
|
||||
|
||||
* sd-boot/sd-stub: install a uefi "handle" to a sidecar dir of bls type #1
|
||||
entries with an "uki" or "uki-url" stanza, and make sd-stub look for
|
||||
that. That way we can parameterize type #1 entries nicely.
|
||||
@@ -212,19 +209,11 @@ Features:
|
||||
own image loading code paths. Given that everything's statically linked
|
||||
anyway on UEFI it should be easy to just jump into the already loaded image.
|
||||
|
||||
* introduce smbios type 11 variables for inserting additional entries into boot
|
||||
loader spec type 1 menu list. Usecase is in particular with http boot: allow
|
||||
VMMs to insert additional cheap payloads that are acquired on demand via
|
||||
http. for example: always install diskomator or such.
|
||||
|
||||
* storagetm: maybe also serve the specified disk via HTTP? we have glue for
|
||||
microhttpd anyway already. Idea would also be serve currently booted UKI as
|
||||
separate HTTP resource, so that EFI http boot on another system could
|
||||
directly boot from our system, with full access to the hdd.
|
||||
|
||||
* support boot-into-tarball in systemd-import-generator: optionally bind mount
|
||||
downloaded image to /sysroot/
|
||||
|
||||
* support specifying download hash sum in systemd-import-generator expression
|
||||
to pin image/tarball.
|
||||
|
||||
@@ -274,14 +263,6 @@ Features:
|
||||
* systemd-cryptenroll: add --firstboot or so, that will interactively ask user
|
||||
whether recovery key shall be enrolled and do so
|
||||
|
||||
* sd-boot: when looking for a BLS type #1 resource and it cannot be found in
|
||||
the ESP check if ESP is backed by ramdisk/http and then request it from same
|
||||
http base. Then: make mkosi build a 2nd esp maybe called the "netesp" that
|
||||
contains bls type #1 entries pointing to the UKIs which would then be
|
||||
requested via http this way. also set an efi var indicating that this
|
||||
happened, so that initrd can recognize this and take into consdiration when
|
||||
looking for root fs
|
||||
|
||||
* bootctl: add tool for registering BootXXX entry that boots from some http
|
||||
server of your choice (i.e. like kernel-bootcfg --add-uri=)
|
||||
|
||||
@@ -1044,30 +1025,12 @@ Features:
|
||||
into two of the three PAM stacks gdm provides.
|
||||
See discussion at https://github.com/authselect/authselect/pull/311
|
||||
|
||||
* sd-boot: make boot loader spec type #1 accept http urls in "linux"
|
||||
lines. Then, do the uefi http dance to download kernels and boot them. This
|
||||
is then useful for network boot, by embedding a cpio with type #1 snippets
|
||||
in sd-boot, which reference remote kernels.
|
||||
|
||||
* maybe prohibit setuid() to the nobody user, to lock things down, via seccomp.
|
||||
the nobody is not a user any code should run under, ever, as that user would
|
||||
possibly get a lot of access to resources it really shouldn't be getting
|
||||
access to due to the userns + nfs semantics of the user. Alternatively: use
|
||||
the seccomp log action, and allow it.
|
||||
|
||||
* sd-boot: add a new PE section .bls or so that carries a cpio with additional
|
||||
boot loader entries (both type1 and type2). Then when initializing, find this
|
||||
section, iterate through it and populate menu with it. cpio is simple enough
|
||||
to make a parser for this reasonably robust. use same path structures as in
|
||||
the ESP. Similar add one for signature key drop-ins.
|
||||
|
||||
* sd-boot: also allow passing in the cpio as in the previous item via SMBIOS
|
||||
|
||||
* add a new EFI tool "sd-fetch" or so. It looks in a PE section ".url" for an
|
||||
URL, then downloads the file from it using UEFI HTTP APIs, and executes it.
|
||||
Use case: provide a minimal ESP with sd-boot and a couple of these sd-fetch
|
||||
binaries in place of UKIs, and download them on-the-fly.
|
||||
|
||||
* maybe: systemd-loop-generator that sets up loopback devices if requested via kernel
|
||||
cmdline. use case: include encrypted/verity root fs in UKI.
|
||||
|
||||
@@ -1307,9 +1270,6 @@ Features:
|
||||
* systemd-measure tool:
|
||||
- pre-calculate PCR 12 (command line) + PCR 13 (sysext) the same way we can precalculate PCR 11
|
||||
|
||||
* in sd-boot: load EFI drivers from a new PE section. That way, one can have a
|
||||
"supercharged" sd-boot binary, that could carry ext4 drivers built-in.
|
||||
|
||||
* sd-device: add an API for acquiring list of child devices, given a device
|
||||
objects (i.e. all child dirents that dirs or symlinks to dirs)
|
||||
|
||||
@@ -1503,27 +1463,6 @@ Features:
|
||||
|
||||
* sd-event: add 1st class event source for timezone changes
|
||||
|
||||
* support uefi/http boots with sd-boot: instead of looking for dropin files in
|
||||
/loader/entries/ dir, look for a file /loader/entries/SHA256SUMS and use that
|
||||
as directory manifest. The file would be a standard directory listing as
|
||||
generated by GNU sha256sums.
|
||||
|
||||
* sd-boot: maybe add support for embedding the various auxiliary resources we
|
||||
look for right in the sd-boot binary. i.e. take inspiration from sd-stub
|
||||
logic: allow combining sd-boot via ukify with kernels to enumerate, .conf
|
||||
files, drivers, keys to enroll and so on. Then, add whatever we find that way
|
||||
to the menu. Use case: allow building a single PE image you can boot into via
|
||||
UEFI HTTP boot.
|
||||
|
||||
* maybe add a new UEFI stub binary "sd-http". It works similar to sd-stub, but
|
||||
all it does is download a file from a http server, and execute it, after
|
||||
optionally checking its hash sum. idea would be: combine this "sd-http" stub
|
||||
binary with some minimal info about a URL + hash sum, plus .osrel data, and
|
||||
drop it into the unified kernel dir in the ESP. And bam you have something
|
||||
that is tiny, feels a lot like a unified kernel, but all it does is chainload
|
||||
the real kernel. benefit: downloading these stubs would be tiny and quick,
|
||||
hence cheap for enumeration.
|
||||
|
||||
* sysext: measure all activated sysext into a TPM PCR
|
||||
|
||||
* systemd-dissect: show available versions inside of a disk image, i.e. if
|
||||
|
||||
@@ -97,6 +97,9 @@ variables. All EFI variables use the vendor UUID
|
||||
generally only written once, by the OS installer, and is usually not touched
|
||||
after that.
|
||||
|
||||
* The EFI variable `LoaderDeviceURL` contains the URL the boot loader was
|
||||
downloaded from, in UTF-16 format. Only set in case of network boots.
|
||||
|
||||
If `LoaderTimeInitUSec` and `LoaderTimeExecUSec` are set, `systemd-analyze`
|
||||
will include them in its boot-time analysis. If `LoaderDevicePartUUID` is set,
|
||||
systemd will mount the ESP that was used for the boot to `/boot`, but only if
|
||||
|
||||
@@ -997,6 +997,7 @@ manpages = [
|
||||
['systemd-keyutil', '1', [], ''],
|
||||
['systemd-localed.service', '8', ['systemd-localed'], 'ENABLE_LOCALED'],
|
||||
['systemd-logind.service', '8', ['systemd-logind'], 'ENABLE_LOGIND'],
|
||||
['systemd-loop@.service', '8', [], ''],
|
||||
['systemd-machine-id-commit.service', '8', [], ''],
|
||||
['systemd-machine-id-setup', '1', [], ''],
|
||||
['systemd-machined.service', '8', ['systemd-machined'], 'ENABLE_MACHINED'],
|
||||
|
||||
@@ -74,6 +74,16 @@
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>io.systemd.boot-entries.extra:</varname><replaceable>ID=DEFINITION</replaceable></term>
|
||||
|
||||
<listitem><para>This allows inserting additional entries into the <command>systemd-boot</command>
|
||||
menu. For details see
|
||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry></para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@@ -440,6 +440,16 @@
|
||||
<xi:include href="version-info.xml" xpointer="v220"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderDeviceURL</varname></term>
|
||||
|
||||
<listitem><para>If the boot loader has been invoked via network booting this variable contains the
|
||||
originating URL. This may be used to automatically acquire additional resources from the same
|
||||
source.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderEntries</varname></term>
|
||||
|
||||
@@ -584,6 +594,28 @@
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>io.systemd.boot-entries.extra:</varname><replaceable>ID=DEFINITION</replaceable></term>
|
||||
|
||||
<listitem><para>This allows inserting additional entries into the <command>systemd-boot</command>
|
||||
menu. Take a pair of menu entry identifier and menu entry definition string. The former should be
|
||||
suitable for use as a filename of a Boot Loader Specification Type #1 entry filename (note that it is
|
||||
used for identification purposes only, no file of this name is actually accessed), the latter shall
|
||||
follow the syntax of the contents of a Type #1 entry. Any menu entry defined this way is processed
|
||||
and shown in pretty much the same way as a Type #1 entry read from the ESP or XBOOTLDR
|
||||
partition. Example:</para>
|
||||
|
||||
<programlisting>io.systemd.boot-entries.extra:fooos-current.conf=title FooOS (Current)
|
||||
uki-url http://example.com/somedir/fooos.efi</programlisting>
|
||||
|
||||
<para>Note that this example contains a newline character. When generating this string from a shell
|
||||
care must be taken to encode it correctly.</para>
|
||||
|
||||
<para>Pass multiple strings formatted this way to generate multiple menu entries.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
||||
+25
-3
@@ -206,9 +206,12 @@
|
||||
|
||||
<listitem><para>Attach the specified disk image to an automatically allocated loopback block device,
|
||||
and print the path to the loopback block device to standard output. This is similar to an invocation
|
||||
of <command>losetup --find --show</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. Takes a path to a disk image file.</para>
|
||||
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>
|
||||
|
||||
<para>If combined with <option>--quiet</option> output of the block device name is suppressed.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
|
||||
</varlistentry>
|
||||
@@ -517,6 +520,15 @@
|
||||
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--loop-ref-auto</option></term>
|
||||
|
||||
<listitem><para>Similar to <option>--loop-ref=</option>, but automatically derive the reference
|
||||
string from the specified backing filename, truncating it if necessary.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--mtree-hash=no</option></term>
|
||||
|
||||
@@ -547,6 +559,16 @@
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--quiet</option></term>
|
||||
<term><option>-q</option></term>
|
||||
|
||||
<listitem><para>If combined with <option>--attach</option>, suppresses output of the used loopback
|
||||
block device path.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="image-policy-open" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-legend" />
|
||||
|
||||
@@ -99,6 +99,10 @@
|
||||
separate, immutable <filename>/usr/</filename> file system. Also see
|
||||
<varname>systemd.volatile=</varname> below.</para>
|
||||
|
||||
<para>Use <literal>bind:…</literal> to bind mount another directory as operating system root
|
||||
filesystems (added in v258). Expects an absolute path name referencing an existing directory within the initrd's file
|
||||
hierarchy to boot into.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v217"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -50,19 +50,49 @@
|
||||
<para>If the units this generator creates are overridden, for example by units in directories with higher
|
||||
precedence, drop-ins and additional dependencies created by this generator might still be used.</para>
|
||||
|
||||
<para>This generator will only look for the root partition on the same physical disk where the EFI System
|
||||
Partition (ESP) is located. Note that support from the boot loader is required: the EFI variable
|
||||
<varname>LoaderDevicePartUUID</varname> of the <constant>4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</constant>
|
||||
vendor UUID is used to determine from which partition, and hence the disk, from which the system was
|
||||
booted. If the boot loader does not set this variable, this generator will not be able to detect the root
|
||||
partition. See the <ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>
|
||||
for details.</para>
|
||||
<para>When run in the initial RAM disk (initrd) this generator can automatically search for the root file
|
||||
system. Specifically:
|
||||
|
||||
<para>Similarly, this generator will only look for the other partitions on the same physical disk as the
|
||||
root partition. In this case, boot loader support is not required. These partitions will not be searched
|
||||
for on systems where the root file system is distributed on multiple disks, for example via btrfs RAID.
|
||||
<itemizedlist>
|
||||
<listitem><para>It will look for the root partition on the same physical disk where the EFI System
|
||||
Partition (ESP) is located. Note that support from the boot loader is required for this to work: the
|
||||
EFI variable <varname>LoaderDevicePartUUID</varname> of the
|
||||
<constant>4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</constant> vendor UUID is used to determine from which
|
||||
partition (and hence disk) the system was booted. If the boot loader does not set this variable, this
|
||||
generator will not be able to detect the root partition. See the <ulink
|
||||
url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink> for
|
||||
details.</para></listitem>
|
||||
|
||||
<listitem><para>Alternatively, it will look for the root file system on a loopback block device whose
|
||||
<literal>.lo_name</literal> field is set to one of the literal strings <literal>rootdisk</literal> or
|
||||
<literal>rootdisk.raw</literal>. This field can be set via <command>losetup</command>'s
|
||||
<option>--loop-ref=</option> string. For images downloaded via
|
||||
<citerefentry><refentrytitle>systemd-import-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
make sure to set the <literal>blockdev</literal> option and set the local name string to
|
||||
<literal>rootdisk</literal> to achieve this effect. Note that discovery of the root file system on
|
||||
loopback block devices like this is only done if <literal>root=gpt-auto</literal> is specified
|
||||
explicitly on the kernel command line, unlike the discovery based on the boot loader reported ESP which
|
||||
is also enabled if no <literal>root=</literal> parameter is specified at all. (The latter relies on
|
||||
<command>systemd-udevd.service</command>'s <filename>/dev/gpt-auto-root</filename> block device symlink
|
||||
generation).</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>When run on the host system (i.e. after successfully transitioning out of the initrd into the root
|
||||
filesystem) this generator will look for all other partitions on the same physical disk as the root
|
||||
partition. For this discovery, boot loader support is not required. Moreover, it is not required that the
|
||||
root partition was automatically discovered by the initrd (as described above) for the discovery of the
|
||||
non-root file partitions to take place. Or in other words: automatic discovery of the root file system
|
||||
and of the non-root file systems are independent operations, that do not rely on each other, and are done
|
||||
during two distinct phases of the boot process (one in the initrd, the other after). These partitions will
|
||||
not be searched for on systems where the root file system is distributed on multiple disks, for example
|
||||
via btrfs RAID.</para>
|
||||
|
||||
<para>The root partition can be configured explicitly by symlinking
|
||||
<filename>/run/systemd/volatile-root</filename> to <filename>/dev/block/$major:$minor</filename>. This is
|
||||
especially useful if the root mount has been replaced by some form of volatile file system
|
||||
(overlayfs).</para>
|
||||
|
||||
<para><filename>systemd-gpt-auto-generator</filename> is useful for centralizing file system
|
||||
configuration in the partition table and making configuration in <filename>/etc/fstab</filename> or on
|
||||
the kernel command line unnecessary.</para>
|
||||
@@ -225,11 +255,6 @@
|
||||
<citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
<para>The root partition can be specified by symlinking <filename>/run/systemd/volatile-root</filename>
|
||||
to <filename>/dev/block/$major:$minor</filename>. This is especially useful if the root mount has been
|
||||
replaced by some form of volatile file system (overlayfs).
|
||||
</para>
|
||||
|
||||
<para>Mount and automount units for the EFI System Partition (ESP) and Extended Boot Loader Partition
|
||||
(XBOOTLDR) are generated on EFI systems. If the disk contains an XBOOTLDR partition, as defined in the
|
||||
<ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader
|
||||
@@ -297,10 +322,14 @@
|
||||
<term><varname>rootflags=</varname></term>
|
||||
|
||||
<listitem><para>When <varname>root=</varname> is used with the special value
|
||||
<literal>gpt-auto</literal> (or if the parameter is not used at all), automatic discovery of the root
|
||||
<literal>gpt-auto</literal>, full automatic discovery of the root
|
||||
partition based on the GPT partition type is enabled. Any other value disables this
|
||||
logic.</para>
|
||||
|
||||
<para>If <varname>root=</varname> is not specified at all on the kernel command line automatic
|
||||
discovery of the root partition via the boot loader reported ESP is also enabled, however in this
|
||||
case discovery based on the loopback block device <literal>.lo_name</literal> field is not enabled.</para>
|
||||
|
||||
<para>The <varname>rootfstype=</varname> and <varname>rootflags=</varname> are used to select the
|
||||
file system type and options when the root file system is automatically discovered.</para>
|
||||
|
||||
|
||||
@@ -117,6 +117,55 @@
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>blockdev</term>
|
||||
|
||||
<listitem><para>If this option is specified the downloaded image is attached to a loopback block
|
||||
device (via <filename>systemd-loop@.service</filename>) after completion. This permits booting
|
||||
from downloaded disk images. This is only supported for <literal>raw</literal> disk images.</para>
|
||||
|
||||
<para>Note when this option is used with the purpose of mounting a disk image conforming to the
|
||||
<ulink url="https://uapi-group.org/specifications/specs/discoverable_disk_image/">Discoverable
|
||||
Disk Image Specification</ulink> as root file system, and the automatic GPT partition discovery
|
||||
logic as implemented by
|
||||
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
shall process it, it's essential to specify <literal>rootdisk</literal> as the local name for the
|
||||
import. Moreover, <literal>root=gpt-auto</literal> must be specified on the kernel command line
|
||||
explicitly. Also, prefix the <literal>systemd.pull=</literal> commmand line option with
|
||||
<literal>rd.</literal> to ensure it is executed in the initial RAM disk (initrd) already, also
|
||||
see below.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>bootorigin</term>
|
||||
|
||||
<listitem><para>If this option is specified, in place of the URL a simple filename may be
|
||||
specified. If the system is booted via UEFI HTTP network booting the last component of the
|
||||
network boot origin URL is replaced by this filename and used as download source. This hence
|
||||
allows to automatically derive the URLs for disk images from the original boot URL used to invoke
|
||||
the kernel or boot loader.</para>
|
||||
|
||||
<para>If this option is used and the system is not actually booted via UEFI HTTP network booting,
|
||||
the download is gracefully skipped. Or in other words without other modifications it is possible
|
||||
to put together an initrd image that will boot from a local disk if available, or from downloaded
|
||||
disk image if used via network booting.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>runtime=</term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. If set to true, the image is downloaded below the
|
||||
<filename>/run/</filename> hierarchy, if set to false below the <filename>/var/lib/</filename>
|
||||
hierarchy. If not specified defaults to true in the initial RAM disk (initrd) and to false on the
|
||||
host system.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
@@ -136,6 +185,10 @@
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>These kernel command line options are interpreted by the host system only. If these options are
|
||||
prefixed with <literal>rd.</literal> they are interpreted by the initial RAM disk (initrd)
|
||||
instead.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
@@ -179,6 +232,39 @@
|
||||
validation. This is useful for development purposes in virtual machines and containers. Warning: do not
|
||||
deploy a system with validation disabled like this!</para>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Download root disk image (raw) into memory, for booting into it</title>
|
||||
|
||||
<programlisting>rd.systemd.pull=raw,machine,verify=no,blockdev:image:https://example.com/image.raw.xz root=/dev/disk/by-loop-ref/image.raw-part2</programlisting>
|
||||
|
||||
<para>This downloads the specified disk image, saving it locally under the name
|
||||
<literal>image</literal>, and attaches it to a loopback block device on completion. It then boots from
|
||||
the 2nd partition in the image.</para>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Boot into disk image (raw), with URL derived from UEFI HTTP network booting</title>
|
||||
|
||||
<programlisting>rd.systemd.pull=raw,machine,verify=no,blockdev,bootorigin:rootdisk:image.raw.xz root=gpt-auto</programlisting>
|
||||
|
||||
<para>This is similar to the previous example, but this time the source URL is automatically derived
|
||||
from the UEFI HTTP network boot URL. For example, if an UKI is booted from an URL
|
||||
<literal>http://example.com/image.efi</literal> this would result in a root disk being downloaded from
|
||||
<literal>http://example.com/image.raw.xz</literal>. Moreover this uses the
|
||||
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
logic to mount the root file system from the disk image.</para>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Boot into disk image (tar), with URL derived from UEFI HTTP network booting</title>
|
||||
|
||||
<programlisting>rd.systemd.pull=tar,machine,verify=no,bootorigin:root:image.tar.xz root=bind:/run/machines/root</programlisting>
|
||||
|
||||
<para>This is similar to the previous example, but instead of a raw (i.e. block device based) disk
|
||||
image the system boots into a tarball that is downloaded from the originating UEFI network
|
||||
server.</para>
|
||||
</example>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
@@ -189,6 +275,8 @@
|
||||
<member><citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-loop@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
<refentry id="systemd-loop_.service">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-loop@.service</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-loop@.service</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-loop@.service</refname>
|
||||
<refpurpose>Attach a loopback block device</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>systemd-loop@<replaceable>path</replaceable>.service</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-loop@.service</filename> is a template service that may be used to automatically
|
||||
attach a loopback block device at boot (or later). Its instance string may reference an (escaped) file
|
||||
system path pointing to a disk image to attach as loopback block device. Use
|
||||
<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
|
||||
specified files, see
|
||||
<citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
|
||||
details.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-import-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -550,6 +550,16 @@
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>StubDeviceURL</varname></term>
|
||||
|
||||
<listitem><para>If the kernel image has been invoked via network booting this variable contains the
|
||||
originating URL. This may be used to automatically acquire additional resources from the same
|
||||
source.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>StubInfo</varname></term>
|
||||
|
||||
|
||||
@@ -265,6 +265,18 @@
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--smbios11=<replaceable>STRING</replaceable></option></term>
|
||||
<term><option>-s <replaceable>STRING</replaceable></option></term>
|
||||
|
||||
<listitem><para>Passes the specified string into the VM as an SMBIOS Type #11 vendor string. This
|
||||
is useful to parameterize the invoked VM in various ways. See
|
||||
<citerefentry><refentrytitle>smbios-type-11</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
for details.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
|
||||
|
||||
@@ -386,6 +386,16 @@
|
||||
directly.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><filename>imports.target</filename></term>
|
||||
<listitem>
|
||||
<para>A target unit that pulls in all disk image download jobs to execute on system boot. This is
|
||||
used by
|
||||
<citerefentry><refentrytitle>systemd-import-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><filename>init.scope</filename></term>
|
||||
<listitem>
|
||||
@@ -1075,6 +1085,16 @@
|
||||
<xi:include href="version-info.xml" xpointer="v235"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><filename>imports-pre.target</filename></term>
|
||||
<listitem>
|
||||
<para>A passive unit that is ordered before all disk image download jobs to execute on system
|
||||
boot. This is used by
|
||||
<citerefentry><refentrytitle>systemd-import-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><filename>local-fs-pre.target</filename></term>
|
||||
<listitem>
|
||||
|
||||
@@ -53,3 +53,5 @@ Packages=
|
||||
tgt
|
||||
tpm2-tools
|
||||
vim
|
||||
# kernel-bootcfg --add-uri= is just too useful
|
||||
virt-firmware
|
||||
|
||||
@@ -59,3 +59,5 @@ Packages=
|
||||
tpm2-tools
|
||||
veritysetup
|
||||
vim-common
|
||||
# kernel-bootcfg --add-uri= is just too useful
|
||||
virt-firmware
|
||||
|
||||
@@ -68,6 +68,8 @@ Packages=
|
||||
psmisc
|
||||
python3-pexpect
|
||||
python3-psutil
|
||||
# kernel-bootcfg --add-uri= is just too useful
|
||||
python3-virt-firmware
|
||||
qrencode
|
||||
quota
|
||||
softhsm2
|
||||
|
||||
@@ -79,5 +79,7 @@ Packages=
|
||||
tpm2.0-tools
|
||||
veritysetup
|
||||
vim
|
||||
# kernel-bootcfg --add-uri= is just too useful
|
||||
virt-firmware
|
||||
xz
|
||||
zypper
|
||||
|
||||
@@ -12,3 +12,7 @@ VolatilePackages=
|
||||
systemd
|
||||
systemd-libs
|
||||
systemd-udev
|
||||
# Pull in systemd-container so that the import-generator is available
|
||||
systemd-container
|
||||
# Pull in systemd-networkd so that we can do network boot
|
||||
systemd-networkd
|
||||
|
||||
@@ -14,6 +14,8 @@ VolatilePackages=
|
||||
libsystemd0
|
||||
libudev1
|
||||
systemd
|
||||
# Pull in systemd-container so that the import-generator is available
|
||||
systemd-container
|
||||
systemd-cryptsetup
|
||||
systemd-repart
|
||||
udev
|
||||
|
||||
@@ -14,5 +14,8 @@ VolatilePackages=
|
||||
libudev1
|
||||
systemd
|
||||
systemd-boot # TODO: Drop once opensuse moves the systemd-stub tmpfiles snippet back to the systemd package
|
||||
# Pull in systemd-container so that the import-generator is available
|
||||
systemd-container
|
||||
systemd-experimental
|
||||
systemd-network
|
||||
udev
|
||||
|
||||
+316
-61
@@ -18,6 +18,7 @@
|
||||
#include "pe.h"
|
||||
#include "proto/block-io.h"
|
||||
#include "proto/device-path.h"
|
||||
#include "proto/load-file.h"
|
||||
#include "proto/simple-text-io.h"
|
||||
#include "random-seed.h"
|
||||
#include "sbat.h"
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "ticks.h"
|
||||
#include "tpm2-pcr.h"
|
||||
#include "uki.h"
|
||||
#include "url-discovery.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
#include "vmm.h"
|
||||
@@ -47,13 +49,26 @@ DECLARE_SBAT(SBAT_BOOT_SECTION_TEXT);
|
||||
typedef enum LoaderType {
|
||||
LOADER_UNDEFINED,
|
||||
LOADER_AUTO,
|
||||
LOADER_EFI,
|
||||
LOADER_LINUX, /* Boot loader spec type #1 entries */
|
||||
LOADER_UNIFIED_LINUX, /* Boot loader spec type #2 entries */
|
||||
LOADER_EFI, /* Boot loader spec type #1 entries with "efi" line */
|
||||
LOADER_LINUX, /* Boot loader spec type #1 entries with "linux" line */
|
||||
LOADER_UKI, /* Boot loader spec type #1 entries with "uki" line */
|
||||
LOADER_UKI_URL, /* Boot loader spec type #1 entries with "uki-url" line */
|
||||
LOADER_TYPE2_UKI, /* Boot loader spec type #2 entries */
|
||||
LOADER_SECURE_BOOT_KEYS,
|
||||
LOADER_BAD, /* Marker: this boot loader spec type #1 entry is invalid */
|
||||
LOADER_IGNORE, /* Marker: this boot loader spec type #1 entry does not match local host */
|
||||
_LOADER_TYPE_MAX,
|
||||
} LoaderType;
|
||||
|
||||
/* Which loader types permit command line editing */
|
||||
#define LOADER_TYPE_ALLOW_EDITOR(t) IN_SET(t, LOADER_EFI, LOADER_LINUX, LOADER_UKI, LOADER_UKI_URL, LOADER_TYPE2_UKI)
|
||||
|
||||
/* Which loader types allow command line editing in SecureBoot mode */
|
||||
#define LOADER_TYPE_ALLOW_EDITOR_IN_SB(t) IN_SET(t, LOADER_EFI, LOADER_LINUX)
|
||||
|
||||
/* Which loader types shall be considered for automatic selection */
|
||||
#define LOADER_TYPE_MAY_AUTO_SELECT(t) IN_SET(t, LOADER_EFI, LOADER_LINUX, LOADER_UKI, LOADER_UKI_URL, LOADER_TYPE2_UKI)
|
||||
|
||||
typedef struct {
|
||||
char16_t *id; /* The unique identifier for this entry (typically the filename of the file defining the entry, possibly suffixed with a profile id) */
|
||||
char16_t *id_without_profile; /* same, but without any profile id suffixed */
|
||||
@@ -65,6 +80,7 @@ typedef struct {
|
||||
EFI_HANDLE *device;
|
||||
LoaderType type;
|
||||
char16_t *loader;
|
||||
char16_t *url;
|
||||
char16_t *devicetree;
|
||||
char16_t *options;
|
||||
bool options_implied; /* If true, these options are implied if we invoke the PE binary without any parameters (as in: UKI). If false we must specify these options explicitly. */
|
||||
@@ -608,6 +624,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
|
||||
printf(" device: %ls\n", dp_str);
|
||||
if (entry->loader)
|
||||
printf(" loader: %ls\n", entry->loader);
|
||||
if (entry->url)
|
||||
printf(" url: %ls\n", entry->url);
|
||||
STRV_FOREACH(initrd, entry->initrd)
|
||||
printf(" initrd: %ls\n", *initrd);
|
||||
if (entry->devicetree)
|
||||
@@ -994,7 +1012,7 @@ static bool menu_run(
|
||||
case KEYPRESS(0, 0, 'E'):
|
||||
/* only the options of configured entries can be edited */
|
||||
if (!config->editor ||
|
||||
!IN_SET(config->entries[idx_highlight]->type, LOADER_EFI, LOADER_LINUX, LOADER_UNIFIED_LINUX)) {
|
||||
!LOADER_TYPE_ALLOW_EDITOR(config->entries[idx_highlight]->type)) {
|
||||
status = xstrdup16(u"Entry does not support editing the command line.");
|
||||
break;
|
||||
}
|
||||
@@ -1002,7 +1020,7 @@ static bool menu_run(
|
||||
/* Unified kernels that are signed as a whole will not accept command line options
|
||||
* when secure boot is enabled unless there is none embedded in the image. Do not try
|
||||
* to pretend we can edit it to only have it be ignored. */
|
||||
if (config->entries[idx_highlight]->type == LOADER_UNIFIED_LINUX &&
|
||||
if (!LOADER_TYPE_ALLOW_EDITOR_IN_SB(config->entries[idx_highlight]->type) &&
|
||||
secure_boot_enabled() &&
|
||||
config->entries[idx_highlight]->options) {
|
||||
status = xstrdup16(u"Entry not editable in SecureBoot mode.");
|
||||
@@ -1199,6 +1217,7 @@ static BootEntry* boot_entry_free(BootEntry *entry) {
|
||||
free(entry->version);
|
||||
free(entry->machine_id);
|
||||
free(entry->loader);
|
||||
free(entry->url);
|
||||
free(entry->devicetree);
|
||||
free(entry->options);
|
||||
strv_free(entry->initrd);
|
||||
@@ -1445,7 +1464,6 @@ static void boot_entry_add_type1(
|
||||
assert(config);
|
||||
assert(device);
|
||||
assert(root_dir);
|
||||
assert(path);
|
||||
assert(file);
|
||||
assert(content);
|
||||
|
||||
@@ -1473,26 +1491,76 @@ static void boot_entry_add_type1(
|
||||
entry->machine_id = xstr8_to_16(value);
|
||||
|
||||
} else if (streq8(key, "linux")) {
|
||||
|
||||
if (!IN_SET(entry->type, LOADER_UNDEFINED, LOADER_LINUX)) {
|
||||
entry->type = LOADER_BAD;
|
||||
break;
|
||||
}
|
||||
|
||||
free(entry->loader);
|
||||
entry->type = LOADER_LINUX;
|
||||
entry->loader = xstr8_to_path(value);
|
||||
entry->key = 'l';
|
||||
|
||||
} else if (streq8(key, "uki")) {
|
||||
|
||||
if (!IN_SET(entry->type, LOADER_UNDEFINED, LOADER_UKI)) {
|
||||
entry->type = LOADER_BAD;
|
||||
break;
|
||||
}
|
||||
|
||||
free(entry->loader);
|
||||
entry->type = LOADER_UKI;
|
||||
entry->loader = xstr8_to_path(value);
|
||||
entry->key = 'l';
|
||||
|
||||
} else if (streq8(key, "uki-url")) {
|
||||
|
||||
if (!IN_SET(entry->type, LOADER_UNDEFINED, LOADER_UKI_URL)) {
|
||||
entry->type = LOADER_BAD;
|
||||
break;
|
||||
}
|
||||
|
||||
_cleanup_free_ char16_t *p = xstr8_to_16(value);
|
||||
|
||||
const char16_t *e = startswith(p, u":");
|
||||
if (e) {
|
||||
_cleanup_free_ char16_t *origin = disk_get_url(device);
|
||||
|
||||
if (!origin) {
|
||||
/* Automatically hide entries that require an original URL but where none is available. */
|
||||
entry->type = LOADER_IGNORE;
|
||||
break;
|
||||
}
|
||||
|
||||
entry->url = url_replace_last_component(origin, p);
|
||||
} else
|
||||
entry->url = TAKE_PTR(p);
|
||||
|
||||
entry->type = LOADER_UKI_URL;
|
||||
entry->key = 'l';
|
||||
|
||||
} else if (streq8(key, "efi")) {
|
||||
|
||||
if (!IN_SET(entry->type, LOADER_UNDEFINED, LOADER_EFI)) {
|
||||
entry->type = LOADER_BAD;
|
||||
break;
|
||||
}
|
||||
|
||||
entry->type = LOADER_EFI;
|
||||
free(entry->loader);
|
||||
entry->loader = xstr8_to_path(value);
|
||||
|
||||
/* do not add an entry for ourselves */
|
||||
if (strcaseeq16(entry->loader, loaded_image_path)) {
|
||||
entry->type = LOADER_UNDEFINED;
|
||||
entry->type = LOADER_IGNORE;
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (streq8(key, "architecture")) {
|
||||
/* do not add an entry for an EFI image of architecture not matching with that of the image */
|
||||
if (!strcaseeq8(value, EFI_MACHINE_TYPE_NAME)) {
|
||||
entry->type = LOADER_UNDEFINED;
|
||||
entry->type = LOADER_IGNORE;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1520,14 +1588,17 @@ static void boot_entry_add_type1(
|
||||
entry->options = TAKE_PTR(new);
|
||||
}
|
||||
|
||||
if (entry->type == LOADER_UNDEFINED)
|
||||
/* Filter all entries that are badly defined or don't apply to the local system. */
|
||||
if (IN_SET(entry->type, LOADER_UNDEFINED, LOADER_BAD, LOADER_IGNORE))
|
||||
return;
|
||||
|
||||
/* check existence */
|
||||
_cleanup_file_close_ EFI_FILE *handle = NULL;
|
||||
err = root_dir->Open(root_dir, &handle, entry->loader, EFI_FILE_MODE_READ, 0ULL);
|
||||
if (err != EFI_SUCCESS)
|
||||
return;
|
||||
/* Check existence of loader file */
|
||||
if (entry->loader) {
|
||||
_cleanup_file_close_ EFI_FILE *handle = NULL;
|
||||
err = root_dir->Open(root_dir, &handle, entry->loader, EFI_FILE_MODE_READ, 0ULL);
|
||||
if (err != EFI_SUCCESS)
|
||||
return;
|
||||
}
|
||||
|
||||
entry->device = device;
|
||||
entry->id = xstrdup16(file);
|
||||
@@ -1535,7 +1606,8 @@ static void boot_entry_add_type1(
|
||||
|
||||
config_add_entry(config, entry);
|
||||
|
||||
boot_entry_parse_tries(entry, path, file, u".conf");
|
||||
if (path)
|
||||
boot_entry_parse_tries(entry, path, file, u".conf");
|
||||
TAKE_PTR(entry);
|
||||
}
|
||||
|
||||
@@ -1645,6 +1717,19 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
|
||||
(void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryLastBooted", &config->entry_saved);
|
||||
}
|
||||
|
||||
static bool valid_type1_filename(const char16_t *fname) {
|
||||
assert(fname);
|
||||
|
||||
if (IN_SET(fname[0], u'.', u'\0'))
|
||||
return false;
|
||||
if (!endswith_no_case(fname, u".conf"))
|
||||
return false;
|
||||
if (startswith_no_case(fname, u"auto-"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void config_load_type1_entries(
|
||||
Config *config,
|
||||
EFI_HANDLE *device,
|
||||
@@ -1675,13 +1760,9 @@ static void config_load_type1_entries(
|
||||
if (err != EFI_SUCCESS || !f)
|
||||
break;
|
||||
|
||||
if (f->FileName[0] == '.')
|
||||
continue;
|
||||
if (FLAGS_SET(f->Attribute, EFI_FILE_DIRECTORY))
|
||||
continue;
|
||||
if (!endswith_no_case(f->FileName, u".conf"))
|
||||
continue;
|
||||
if (startswith_no_case(f->FileName, u"auto-"))
|
||||
if (!valid_type1_filename(f->FileName))
|
||||
continue;
|
||||
|
||||
err = file_read(entries_dir,
|
||||
@@ -1697,6 +1778,41 @@ static void config_load_type1_entries(
|
||||
}
|
||||
}
|
||||
|
||||
static void config_load_smbios_entries(
|
||||
Config *config,
|
||||
EFI_HANDLE *device,
|
||||
EFI_FILE *root_dir,
|
||||
const char16_t *loaded_image_path) {
|
||||
|
||||
assert(config);
|
||||
assert(device);
|
||||
assert(root_dir);
|
||||
|
||||
/* Loads Boot Loader Type #1 entries from SMBIOS 11 */
|
||||
|
||||
if (is_confidential_vm())
|
||||
return; /* Don't consume SMBIOS in CoCo contexts */
|
||||
|
||||
for (const char *after = NULL, *extra;; after = extra) {
|
||||
extra = smbios_find_oem_string("io.systemd.boot.entries-extra:", after);
|
||||
if (!extra)
|
||||
break;
|
||||
|
||||
const char *eq = strchr8(extra, '=');
|
||||
if (!eq)
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char16_t *fname = xstrn8_to_16(extra, eq - extra);
|
||||
if (!valid_type1_filename(fname))
|
||||
continue;
|
||||
|
||||
/* Make a copy, since boot_entry_add_type1() wants to modify it */
|
||||
_cleanup_free_ char *contents = xstrdup8(eq + 1);
|
||||
|
||||
boot_entry_add_type1(config, device, root_dir, /* path= */ NULL, fname, contents, loaded_image_path);
|
||||
}
|
||||
}
|
||||
|
||||
static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
|
||||
int r;
|
||||
|
||||
@@ -1806,7 +1922,7 @@ static void config_select_default_entry(Config *config) {
|
||||
|
||||
/* select the first suitable entry */
|
||||
for (i = 0; i < config->n_entries; i++)
|
||||
if (config->entries[i]->type != LOADER_AUTO && !config->entries[i]->call) {
|
||||
if (LOADER_TYPE_MAY_AUTO_SELECT(config->entries[i]->type)) {
|
||||
config->idx_default = i;
|
||||
return;
|
||||
}
|
||||
@@ -2323,7 +2439,7 @@ static void boot_entry_add_type2(
|
||||
*entry = (BootEntry) {
|
||||
.id = strtolower16(TAKE_PTR(id)),
|
||||
.id_without_profile = profile > 0 ? strtolower16(xstrdup16(filename)) : NULL,
|
||||
.type = LOADER_UNIFIED_LINUX,
|
||||
.type = LOADER_TYPE2_UKI,
|
||||
.title = TAKE_PTR(title),
|
||||
.version = xstrdup16(good_version),
|
||||
.device = device,
|
||||
@@ -2523,13 +2639,77 @@ static EFI_STATUS initrd_prepare(
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static EFI_STATUS expand_path(
|
||||
EFI_HANDLE parent_image,
|
||||
EFI_DEVICE_PATH *path,
|
||||
EFI_DEVICE_PATH **ret_expanded_path) {
|
||||
|
||||
EFI_STATUS err;
|
||||
|
||||
assert(parent_image);
|
||||
assert(path);
|
||||
assert(ret_expanded_path);
|
||||
|
||||
_cleanup_free_ EFI_HANDLE *handles = NULL;
|
||||
size_t n_handles = 0;
|
||||
err = BS->LocateHandleBuffer(
|
||||
ByProtocol,
|
||||
MAKE_GUID_PTR(EFI_LOAD_FILE_PROTOCOL),
|
||||
/* SearchKey= */ NULL,
|
||||
&n_handles,
|
||||
&handles);
|
||||
if (!IN_SET(err, EFI_SUCCESS, EFI_NOT_FOUND))
|
||||
return log_error_status(err, "Failed to get list of LoadFile protocol handles: %m");
|
||||
|
||||
FOREACH_ARRAY(h, handles, n_handles) {
|
||||
EFI_LOAD_FILE_PROTOCOL *load_file = NULL;
|
||||
err = BS->OpenProtocol(
|
||||
*h,
|
||||
MAKE_GUID_PTR(EFI_LOAD_FILE_PROTOCOL),
|
||||
(void**) &load_file,
|
||||
parent_image,
|
||||
/* ControllerHandler= */ NULL,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||||
if (IN_SET(err, EFI_NOT_FOUND, EFI_INVALID_PARAMETER))
|
||||
continue; /* Skip over LoadFile() handles that are not suitable for this kind of device path */
|
||||
if (err != EFI_SUCCESS) {
|
||||
log_warning_status(err, "Failed to get LoadFile() protocol, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Issue a LoadFile() request without interest in the actual data (i.e. size is zero and
|
||||
* buffer pointer is NULL), but with BootPolicy set to true, this has the effect of
|
||||
* downloading the URL and establishing a handle for it. */
|
||||
size_t size = 0;
|
||||
err = load_file->LoadFile(load_file, path, /* BootPolicy= */ true, &size, /* Buffer= */ NULL);
|
||||
if (IN_SET(err, EFI_NOT_FOUND, EFI_INVALID_PARAMETER))
|
||||
continue; /* Skip over LoadFile() handles that after all don't consider themselves
|
||||
* appropriate for this kind of path */
|
||||
if (err != EFI_BUFFER_TOO_SMALL) {
|
||||
log_warning_status(err, "Failed to get file via LoadFile() protocol, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Now read the updated file path */
|
||||
EFI_DEVICE_PATH *load_file_path = NULL;
|
||||
err = BS->HandleProtocol(*h, MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL), (void **) &load_file_path);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Failed to get LoadFile() device path: %m");
|
||||
|
||||
/* And return a copy */
|
||||
*ret_expanded_path = device_path_dup(load_file_path);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
static EFI_STATUS image_start(
|
||||
EFI_HANDLE parent_image,
|
||||
const BootEntry *entry) {
|
||||
|
||||
_cleanup_(devicetree_cleanup) struct devicetree_state dtstate = {};
|
||||
_cleanup_(unload_imagep) EFI_HANDLE image = NULL;
|
||||
_cleanup_free_ EFI_DEVICE_PATH *path = NULL;
|
||||
EFI_STATUS err;
|
||||
|
||||
assert(entry);
|
||||
@@ -2539,38 +2719,94 @@ static EFI_STATUS image_start(
|
||||
(void) entry->call();
|
||||
|
||||
_cleanup_file_close_ EFI_FILE *image_root = NULL;
|
||||
err = open_volume(entry->device, &image_root);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error opening root path: %m");
|
||||
|
||||
err = make_file_device_path(entry->device, entry->loader, &path);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error making file device path: %m");
|
||||
|
||||
size_t initrd_size = 0;
|
||||
_cleanup_pages_ Pages initrd_pages = {};
|
||||
_cleanup_free_ char16_t *options_initrd = NULL;
|
||||
err = initrd_prepare(image_root, entry, &options_initrd, &initrd_pages, &initrd_size);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error preparing initrd: %m");
|
||||
|
||||
err = shim_load_image(parent_image, path, &image);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error loading %ls: %m", entry->loader);
|
||||
|
||||
/* DTBs are loaded by the kernel before ExitBootServices, and they can be used to map and assign
|
||||
* arbitrary memory ranges, so skip them when secure boot is enabled as the DTB here is unverified.
|
||||
*/
|
||||
if (entry->devicetree && !secure_boot_enabled()) {
|
||||
err = devicetree_install(&dtstate, image_root, entry->devicetree);
|
||||
_cleanup_free_ EFI_DEVICE_PATH *path = NULL;
|
||||
bool boot_policy;
|
||||
if (entry->url) {
|
||||
/* Generate a device path that only contains the URL */
|
||||
err = make_url_device_path(entry->url, &path);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error loading %ls: %m", entry->devicetree);
|
||||
return log_error_status(err, "Error making URL device path: %m");
|
||||
|
||||
/* Try to expand this path on all available NICs and IP protocols */
|
||||
_cleanup_free_ EFI_DEVICE_PATH *expanded_path = NULL;
|
||||
for (unsigned n_attempt = 0;; n_attempt++) {
|
||||
err = expand_path(parent_image, path, &expanded_path);
|
||||
if (err == EFI_SUCCESS) {
|
||||
/* If this worked then let's try to boot with the expanded path. */
|
||||
free(path);
|
||||
path = TAKE_PTR(expanded_path);
|
||||
break;
|
||||
}
|
||||
if (err != EFI_NOT_FOUND || n_attempt > 5) {
|
||||
log_warning_status(err, "Failed to expand device path, ignoring: %m");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Maybe the network devices have been configured for this yet (because we are the
|
||||
* first piece of code trying to do networking)? Then let's connect them, and try
|
||||
* again. */
|
||||
reconnect_all_drivers();
|
||||
}
|
||||
|
||||
/* Note: if the path expansion doesn't work, we'll continue with the unexpanded path. Which
|
||||
* will probably fail on many (most?) firmwares, but it's worth a try. */
|
||||
|
||||
boot_policy = true; /* Set BootPolicy parameter to LoadImage() to true, which ultimately
|
||||
* controls whether the LoadFile (and thus HTTP boot) or LoadFile2 (which
|
||||
* does not set up HTTP boot) protocol shall be used. */
|
||||
} else {
|
||||
assert(entry->device);
|
||||
assert(entry->loader);
|
||||
|
||||
err = open_volume(entry->device, &image_root);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error opening root path: %m");
|
||||
|
||||
err = make_file_device_path(entry->device, entry->loader, &path);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error making file device path: %m");
|
||||
|
||||
boot_policy = false;
|
||||
}
|
||||
|
||||
/* Authenticate the image before we continue with initrd or DT stuff */
|
||||
err = shim_load_image(parent_image, path, boot_policy, &image);
|
||||
if (err != EFI_SUCCESS) {
|
||||
if (entry->url) {
|
||||
/* EFI_NOT_FOUND typically indicates that no network stack or NIC was available, let's give the user a hint. */
|
||||
if (err == EFI_NOT_FOUND) {
|
||||
log_info("Unable to boot remote UKI %ls, is networking available?", entry->url);
|
||||
return err;
|
||||
}
|
||||
|
||||
return log_error_status(err, "Error loading loading remote UKI %ls: %m", entry->url);
|
||||
}
|
||||
|
||||
return log_error_status(err, "Error loading EFI binary %ls: %m", entry->loader);
|
||||
}
|
||||
|
||||
_cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
|
||||
err = initrd_register(PHYSICAL_ADDRESS_TO_POINTER(initrd_pages.addr), initrd_size, &initrd_handle);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error registering initrd: %m");
|
||||
_cleanup_free_ char16_t *options_initrd = NULL;
|
||||
_cleanup_pages_ Pages initrd_pages = {};
|
||||
size_t initrd_size = 0;
|
||||
if (image_root) {
|
||||
err = initrd_prepare(image_root, entry, &options_initrd, &initrd_pages, &initrd_size);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error preparing initrd: %m");
|
||||
|
||||
/* DTBs are loaded by the kernel before ExitBootServices(), and they can be used to map and
|
||||
* assign arbitrary memory ranges, so skip them when secure boot is enabled as the DTB here
|
||||
* is unverified. */
|
||||
if (entry->devicetree && !secure_boot_enabled()) {
|
||||
err = devicetree_install(&dtstate, image_root, entry->devicetree);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error loading %ls: %m", entry->devicetree);
|
||||
}
|
||||
|
||||
err = initrd_register(PHYSICAL_ADDRESS_TO_POINTER(initrd_pages.addr), initrd_size, &initrd_handle);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error registering initrd: %m");
|
||||
}
|
||||
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
|
||||
err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image);
|
||||
@@ -2583,7 +2819,7 @@ static EFI_STATUS image_start(
|
||||
_cleanup_free_ char16_t *options = xstrdup16(options_initrd ?: entry->options_implied ? NULL : entry->options);
|
||||
|
||||
if (entry->type == LOADER_LINUX && !is_confidential_vm()) {
|
||||
const char *extra = smbios_find_oem_string("io.systemd.boot.kernel-cmdline-extra");
|
||||
const char *extra = smbios_find_oem_string("io.systemd.boot.kernel-cmdline-extra=", /* after= */ NULL);
|
||||
if (extra) {
|
||||
_cleanup_free_ char16_t *tmp = TAKE_PTR(options), *extra16 = xstr8_to_16(extra);
|
||||
if (isempty(tmp))
|
||||
@@ -2636,7 +2872,7 @@ static EFI_STATUS image_start(
|
||||
err = EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return log_error_status(err, "Failed to execute %ls (%ls): %m", entry->title_show, entry->loader);
|
||||
return log_error_status(err, "Failed to execute %ls (%ls): %m", entry->title_show, entry->loader ?: entry->url);
|
||||
}
|
||||
|
||||
static void config_free(Config *config) {
|
||||
@@ -2764,6 +3000,9 @@ static void export_loader_variables(
|
||||
EFI_LOADER_FEATURE_RETAIN_SHIM |
|
||||
EFI_LOADER_FEATURE_MENU_DISABLE |
|
||||
EFI_LOADER_FEATURE_MULTI_PROFILE_UKI |
|
||||
EFI_LOADER_FEATURE_REPORT_URL |
|
||||
EFI_LOADER_FEATURE_TYPE1_UKI |
|
||||
EFI_LOADER_FEATURE_TYPE1_UKI_URL |
|
||||
0;
|
||||
|
||||
assert(loaded_image);
|
||||
@@ -2794,6 +3033,9 @@ static void config_load_all_entries(
|
||||
/* Similar, but on any XBOOTLDR partition */
|
||||
config_load_xbootldr(config, loaded_image->DeviceHandle);
|
||||
|
||||
/* Pick up entries defined via SMBIOS Type #11 */
|
||||
config_load_smbios_entries(config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
|
||||
|
||||
/* Sort entries after version number */
|
||||
sort_pointer_array((void **) config->entries, config->n_entries, (compare_pointer_func_t) boot_entry_compare);
|
||||
|
||||
@@ -2876,21 +3118,34 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
||||
|
||||
init_usec = time_usec();
|
||||
|
||||
/* Ask Shim to leave its protocol around, so that the stub can use it to validate PEs.
|
||||
* By default, Shim uninstalls its protocol when calling StartImage(). */
|
||||
shim_retain_protocol();
|
||||
|
||||
err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m");
|
||||
|
||||
err = discover_root_dir(loaded_image, &root_dir);
|
||||
if (err != EFI_SUCCESS) {
|
||||
log_error_status(err, "Unable to open root directory: %m");
|
||||
|
||||
/* If opening the root directory fails this typically means someone is trying to boot our
|
||||
* systemd-boot EFI PE binary as network boot NBP. That cannot work however, since we
|
||||
* wouldn't find any menu entries. Provide a helpful message what to try instead. */
|
||||
|
||||
if (err == EFI_UNSUPPORTED)
|
||||
log_info("| Note that invoking systemd-boot directly as UEFI network boot NBP is not\n"
|
||||
"| supported. Instead of booting the systemd-boot PE binary (i.e. an .efi file)\n"
|
||||
"| via the network, use an EFI GPT disk image (i.e. a file with .img suffix)\n"
|
||||
"| containing systemd-boot instead.");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Ask Shim to leave its protocol around, so that the stub can use it to validate PEs.
|
||||
* By default, Shim uninstalls its protocol when calling StartImage(). */
|
||||
shim_retain_protocol();
|
||||
|
||||
export_common_variables(loaded_image);
|
||||
export_loader_variables(loaded_image, init_usec);
|
||||
|
||||
err = discover_root_dir(loaded_image, &root_dir);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status(err, "Unable to open root directory: %m");
|
||||
|
||||
(void) load_drivers(image, loaded_image, root_dir);
|
||||
|
||||
_cleanup_free_ char16_t *loaded_image_path = NULL;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "device-path-util.h"
|
||||
#include "efi-string.h"
|
||||
#include "util.h"
|
||||
|
||||
EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp) {
|
||||
@@ -38,6 +39,38 @@ EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DE
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS make_url_device_path(const char16_t *url, EFI_DEVICE_PATH **ret) {
|
||||
assert(url);
|
||||
assert(ret);
|
||||
|
||||
/* Turns a URL into a simple one-element URL device path. */
|
||||
|
||||
_cleanup_free_ char* u = xstr16_to_ascii(url);
|
||||
if (!u)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
size_t l = strlen8(u);
|
||||
|
||||
size_t t = offsetof(URI_DEVICE_PATH, Uri) + l + sizeof(EFI_DEVICE_PATH);
|
||||
EFI_DEVICE_PATH *dp = xmalloc(t);
|
||||
|
||||
URI_DEVICE_PATH *udp = (URI_DEVICE_PATH*) dp;
|
||||
udp->Header = (EFI_DEVICE_PATH) {
|
||||
.Type = MESSAGING_DEVICE_PATH,
|
||||
.SubType = MSG_URI_DP,
|
||||
.Length = offsetof(URI_DEVICE_PATH, Uri) + l,
|
||||
};
|
||||
memcpy(udp->Uri, u, l);
|
||||
|
||||
EFI_DEVICE_PATH *end = device_path_next_node(dp);
|
||||
*end = DEVICE_PATH_END_NODE;
|
||||
|
||||
assert(((uint8_t*) end + sizeof(EFI_DEVICE_PATH)) == ((uint8_t*) dp + t));
|
||||
|
||||
*ret = TAKE_PTR(dp);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static char16_t *device_path_to_str_internal(const EFI_DEVICE_PATH *dp) {
|
||||
char16_t *str = NULL;
|
||||
|
||||
@@ -90,7 +123,7 @@ EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
str = dp_to_text->ConvertDevicePathToText(dp, false, false);
|
||||
str = dp_to_text->ConvertDevicePathToText(dp, /* DisplayOnly=*/ false, /* AllowShortcuts= */ false);
|
||||
if (!str)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
@@ -136,3 +169,12 @@ EFI_DEVICE_PATH *device_path_replace_node(
|
||||
*end = DEVICE_PATH_END_NODE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t device_path_size(const EFI_DEVICE_PATH *dp) {
|
||||
const EFI_DEVICE_PATH *i = ASSERT_PTR(dp);
|
||||
|
||||
for (; !device_path_is_end(i); i = device_path_next_node(i))
|
||||
;
|
||||
|
||||
return (const uint8_t*) i - (const uint8_t*) dp + sizeof(EFI_DEVICE_PATH);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "proto/device-path.h"
|
||||
#include "util.h"
|
||||
|
||||
EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp);
|
||||
EFI_STATUS make_url_device_path(const char16_t *url, EFI_DEVICE_PATH **ret);
|
||||
EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret);
|
||||
bool device_path_startswith(const EFI_DEVICE_PATH *dp, const EFI_DEVICE_PATH *start);
|
||||
EFI_DEVICE_PATH *device_path_replace_node(
|
||||
@@ -25,3 +27,9 @@ static inline bool device_path_is_end(const EFI_DEVICE_PATH *dp) {
|
||||
.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE, \
|
||||
.Length = sizeof(EFI_DEVICE_PATH) \
|
||||
}
|
||||
|
||||
size_t device_path_size(const EFI_DEVICE_PATH *dp);
|
||||
|
||||
static inline EFI_DEVICE_PATH *device_path_dup(const EFI_DEVICE_PATH *dp) {
|
||||
return xmemdup(ASSERT_PTR(dp), device_path_size(dp));
|
||||
}
|
||||
|
||||
+12
-4
@@ -55,15 +55,23 @@ EFI_STATUS reconnect_all_drivers(void) {
|
||||
|
||||
/* Reconnects all handles, so that any loaded drivers can take effect. */
|
||||
|
||||
err = BS->LocateHandleBuffer(AllHandles, NULL, NULL, &n_handles, &handles);
|
||||
if (err != EFI_SUCCESS)
|
||||
err = BS->LocateHandleBuffer(
|
||||
AllHandles,
|
||||
/* Protocol= */ NULL,
|
||||
/* SearchKey= */ NULL,
|
||||
&n_handles, &handles);
|
||||
if (!IN_SET(err, EFI_SUCCESS, EFI_NOT_FOUND))
|
||||
return log_error_status(err, "Failed to get list of handles: %m");
|
||||
|
||||
for (size_t i = 0; i < n_handles; i++)
|
||||
FOREACH_ARRAY(h, handles, n_handles)
|
||||
/* Some firmware gives us some bogus handles (or they might become bad due to
|
||||
* reconnecting everything). Security policy may also prevent us from doing so too.
|
||||
* There is nothing we can realistically do on errors anyways, so just ignore them. */
|
||||
(void) BS->ConnectController(handles[i], NULL, NULL, true);
|
||||
(void) BS->ConnectController(
|
||||
*h,
|
||||
/* DriverImageHandle= */ NULL,
|
||||
/* RemainingDevicePath= */ NULL,
|
||||
/* Recursive= */ true);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
+53
-3
@@ -188,8 +188,10 @@ static unsigned utf8_to_unichar(const char *utf8, size_t n, char32_t *c) {
|
||||
|
||||
/* Convert UTF-8 to UCS-2, skipping any invalid or short byte sequences. */
|
||||
char16_t *xstrn8_to_16(const char *str8, size_t n) {
|
||||
if (!str8 || n == 0)
|
||||
return NULL;
|
||||
assert(str8 || n == 0);
|
||||
|
||||
if (n == SIZE_MAX)
|
||||
n = strlen8(str8);
|
||||
|
||||
size_t i = 0;
|
||||
char16_t *str16 = xnew(char16_t, n + 1);
|
||||
@@ -209,10 +211,34 @@ char16_t *xstrn8_to_16(const char *str8, size_t n) {
|
||||
}
|
||||
}
|
||||
|
||||
str16[i] = '\0';
|
||||
str16[i] = u'\0';
|
||||
return str16;
|
||||
}
|
||||
|
||||
char *xstrn16_to_ascii(const char16_t *str16, size_t n) {
|
||||
assert(str16 || n == 0);
|
||||
|
||||
if (n == SIZE_MAX)
|
||||
n = strlen16(str16);
|
||||
|
||||
_cleanup_free_ char *str8 = xnew(char, n + 1);
|
||||
|
||||
size_t i = 0;
|
||||
while (n > 0 && *str16 != u'\0') {
|
||||
|
||||
if ((uint16_t) *str16 > 127U) /* Not ASCII? Fail! */
|
||||
return NULL;
|
||||
|
||||
str8[i++] = (char) (uint16_t) *str16;
|
||||
|
||||
str16++;
|
||||
n--;
|
||||
}
|
||||
|
||||
str8[i] = '\0';
|
||||
return TAKE_PTR(str8);
|
||||
}
|
||||
|
||||
char* startswith8(const char *s, const char *prefix) {
|
||||
size_t l;
|
||||
|
||||
@@ -1083,3 +1109,27 @@ void *memset(void *p, int c, size_t n) {
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
size_t strspn16(const char16_t *p, const char16_t *good) {
|
||||
assert(p);
|
||||
assert(good);
|
||||
|
||||
const char16_t *i = p;
|
||||
for (; *i != 0; i++)
|
||||
if (!strchr16(good, *i))
|
||||
break;
|
||||
|
||||
return i - p;
|
||||
}
|
||||
|
||||
size_t strcspn16(const char16_t *p, const char16_t *bad) {
|
||||
assert(p);
|
||||
assert(bad);
|
||||
|
||||
const char16_t *i = p;
|
||||
for (; *i != 0; i++)
|
||||
if (strchr16(bad, *i))
|
||||
break;
|
||||
|
||||
return i - p;
|
||||
}
|
||||
|
||||
@@ -98,11 +98,19 @@ static inline char16_t *xstrdup16(const char16_t *s) {
|
||||
|
||||
char16_t *xstrn8_to_16(const char *str8, size_t n);
|
||||
static inline char16_t *xstr8_to_16(const char *str8) {
|
||||
return xstrn8_to_16(str8, strlen8(str8));
|
||||
return xstrn8_to_16(str8, SIZE_MAX);
|
||||
}
|
||||
|
||||
char *xstrn16_to_ascii(const char16_t *str16, size_t n);
|
||||
static inline char *xstr16_to_ascii(const char16_t *str16) {
|
||||
return xstrn16_to_ascii(str16, SIZE_MAX);
|
||||
}
|
||||
|
||||
char* startswith8(const char *s, const char *prefix);
|
||||
|
||||
size_t strspn16(const char16_t *p, const char16_t *good);
|
||||
size_t strcspn16(const char16_t *p, const char16_t *bad);
|
||||
|
||||
bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack);
|
||||
|
||||
bool parse_number8(const char *s, uint64_t *ret_u, const char **ret_tail);
|
||||
|
||||
+13
-5
@@ -4,17 +4,25 @@
|
||||
#include "efivars.h"
|
||||
#include "export-vars.h"
|
||||
#include "part-discovery.h"
|
||||
#include "url-discovery.h"
|
||||
#include "util.h"
|
||||
|
||||
void export_common_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
|
||||
assert(loaded_image);
|
||||
|
||||
/* Export the device path this image is started from, if it's not set yet */
|
||||
if (loaded_image->DeviceHandle &&
|
||||
efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) {
|
||||
_cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle);
|
||||
if (uuid)
|
||||
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0);
|
||||
if (loaded_image->DeviceHandle) {
|
||||
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", /* ret_data= */ NULL, /* ret_size= */ NULL) != EFI_SUCCESS) {
|
||||
_cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle);
|
||||
if (uuid)
|
||||
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0);
|
||||
}
|
||||
|
||||
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDeviceURL", /* ret_data= */ NULL, /* ret_size= */ NULL) != EFI_SUCCESS) {
|
||||
_cleanup_free_ char16_t *url = disk_get_url(loaded_image->DeviceHandle);
|
||||
if (url)
|
||||
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderDeviceURL", url, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the
|
||||
|
||||
@@ -300,6 +300,7 @@ libefi_sources = files(
|
||||
'shim.c',
|
||||
'smbios.c',
|
||||
'ticks.c',
|
||||
'url-discovery.c',
|
||||
'util.c',
|
||||
'vmm.c',
|
||||
)
|
||||
|
||||
@@ -30,6 +30,8 @@ enum {
|
||||
MEDIA_FILEPATH_DP = 0x04,
|
||||
MEDIA_PIWG_FW_FILE_DP = 0x06,
|
||||
MEDIA_PIWG_FW_VOL_DP = 0x07,
|
||||
|
||||
MSG_URI_DP = 24,
|
||||
};
|
||||
|
||||
struct _packed_ EFI_DEVICE_PATH_PROTOCOL {
|
||||
@@ -67,6 +69,11 @@ typedef struct {
|
||||
char16_t PathName[];
|
||||
} _packed_ FILEPATH_DEVICE_PATH;
|
||||
|
||||
typedef struct {
|
||||
EFI_DEVICE_PATH Header;
|
||||
char Uri[];
|
||||
} _packed_ URI_DEVICE_PATH;
|
||||
|
||||
typedef struct {
|
||||
char16_t* (EFIAPI *ConvertDeviceNodeToText)(
|
||||
const EFI_DEVICE_PATH *DeviceNode,
|
||||
|
||||
+12
-3
@@ -81,7 +81,12 @@ static bool shim_validate(
|
||||
return shim_lock->shim_verify(file_buffer, file_size) == EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image) {
|
||||
EFI_STATUS shim_load_image(
|
||||
EFI_HANDLE parent,
|
||||
const EFI_DEVICE_PATH *device_path,
|
||||
bool boot_policy,
|
||||
EFI_HANDLE *ret_image) {
|
||||
|
||||
assert(device_path);
|
||||
assert(ret_image);
|
||||
|
||||
@@ -91,8 +96,12 @@ EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path
|
||||
install_security_override(shim_validate, NULL);
|
||||
|
||||
EFI_STATUS ret = BS->LoadImage(
|
||||
/*BootPolicy=*/false, parent, (EFI_DEVICE_PATH *) device_path, NULL, 0, ret_image);
|
||||
|
||||
/* BootPolicy= */ boot_policy,
|
||||
parent,
|
||||
(EFI_DEVICE_PATH *) device_path,
|
||||
/* SourceBuffer= */ NULL,
|
||||
/* SourceSize= */ 0,
|
||||
ret_image);
|
||||
if (have_shim)
|
||||
uninstall_security_override();
|
||||
|
||||
|
||||
+1
-1
@@ -12,5 +12,5 @@
|
||||
#include "efi.h"
|
||||
|
||||
bool shim_loaded(void);
|
||||
EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image);
|
||||
EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, bool boot_policy, EFI_HANDLE *ret_image);
|
||||
void shim_retain_protocol(void);
|
||||
|
||||
+4
-4
@@ -182,7 +182,7 @@ bool smbios_in_hypervisor(void) {
|
||||
return FLAGS_SET(type0->bios_characteristics_ext[1], 1 << 4);
|
||||
}
|
||||
|
||||
const char* smbios_find_oem_string(const char *name) {
|
||||
const char* smbios_find_oem_string(const char *name, const char *after) {
|
||||
uint64_t left;
|
||||
|
||||
assert(name);
|
||||
@@ -199,9 +199,9 @@ const char* smbios_find_oem_string(const char *name) {
|
||||
if (!e || e == p) /* Double NUL byte means we've reached the end of the OEM strings. */
|
||||
break;
|
||||
|
||||
const char *eq = startswith8(p, name);
|
||||
if (eq && *eq == '=')
|
||||
return eq + 1;
|
||||
const char *suffix = startswith8(p, name);
|
||||
if (suffix && (!after || suffix > after))
|
||||
return suffix;
|
||||
|
||||
p = e + 1;
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
bool smbios_in_hypervisor(void);
|
||||
|
||||
const char* smbios_find_oem_string(const char *name);
|
||||
const char* smbios_find_oem_string(const char *name, const char *after);
|
||||
|
||||
typedef struct RawSmbiosInfo {
|
||||
const char *manufacturer;
|
||||
|
||||
+8
-2
@@ -21,6 +21,7 @@
|
||||
#include "splash.h"
|
||||
#include "tpm2-pcr.h"
|
||||
#include "uki.h"
|
||||
#include "url-discovery.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
#include "vmm.h"
|
||||
@@ -151,6 +152,7 @@ static void export_stub_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image, unsig
|
||||
EFI_STUB_FEATURE_DEVICETREE_ADDONS | /* We pick up .dtb addons */
|
||||
EFI_STUB_FEATURE_MULTI_PROFILE_UKI | /* We grok the "@1" profile command line argument */
|
||||
EFI_STUB_FEATURE_REPORT_STUB_PARTITION | /* We set StubDevicePartUUID + StubImageIdentifier */
|
||||
EFI_STUB_FEATURE_REPORT_URL | /* We set StubDeviceURL + LoaderDeviceURL */
|
||||
0;
|
||||
|
||||
assert(loaded_image);
|
||||
@@ -167,6 +169,10 @@ static void export_stub_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image, unsig
|
||||
_cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle);
|
||||
if (uuid)
|
||||
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"StubDevicePartUUID", uuid, 0);
|
||||
|
||||
_cleanup_free_ char16_t *url = disk_get_url(loaded_image->DeviceHandle);
|
||||
if (url)
|
||||
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"StubDeviceURL", url, 0);
|
||||
}
|
||||
|
||||
if (loaded_image->FilePath) {
|
||||
@@ -592,7 +598,7 @@ static EFI_STATUS load_addons(
|
||||
|
||||
/* By using shim_load_image, we cover both the case where the PE files are signed with MoK
|
||||
* and with DB, and running with or without shim. */
|
||||
err = shim_load_image(stub_image, addon_path, &addon);
|
||||
err = shim_load_image(stub_image, addon_path, /* boot_policy= */ false, &addon);
|
||||
if (err != EFI_SUCCESS) {
|
||||
log_error_status(err,
|
||||
"Failed to read '%ls' from '%ls', ignoring: %m",
|
||||
@@ -777,7 +783,7 @@ static void cmdline_append_and_measure_smbios(char16_t **cmdline, int *parameter
|
||||
if (is_confidential_vm())
|
||||
return;
|
||||
|
||||
const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra");
|
||||
const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra=", /* after= */ NULL);
|
||||
if (!extra)
|
||||
return;
|
||||
|
||||
|
||||
@@ -332,30 +332,70 @@ TEST(xstrdup16) {
|
||||
TEST(xstrn8_to_16) {
|
||||
char16_t *s = NULL;
|
||||
|
||||
assert_se(xstrn8_to_16(NULL, 1) == NULL);
|
||||
assert_se(xstrn8_to_16("a", 0) == NULL);
|
||||
assert_se(s = xstrn8_to_16(NULL, 0));
|
||||
ASSERT_TRUE(streq16(s, u""));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstrn8_to_16("", 0));
|
||||
ASSERT_TRUE(streq16(s, u""));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstrn8_to_16("a", 0));
|
||||
ASSERT_TRUE(streq16(s, u""));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstrn8_to_16("", 1));
|
||||
assert_se(streq16(s, u""));
|
||||
ASSERT_TRUE(streq16(s, u""));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstrn8_to_16("1", 1));
|
||||
assert_se(streq16(s, u"1"));
|
||||
ASSERT_TRUE(streq16(s, u"1"));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstr8_to_16("abcxyzABCXYZ09 .,-_#*!\"§$%&/()=?`~"));
|
||||
assert_se(streq16(s, u"abcxyzABCXYZ09 .,-_#*!\"§$%&/()=?`~"));
|
||||
ASSERT_TRUE(streq16(s, u"abcxyzABCXYZ09 .,-_#*!\"§$%&/()=?`~"));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstr8_to_16("ÿⱿ𝇉 😺"));
|
||||
assert_se(streq16(s, u"ÿⱿ "));
|
||||
ASSERT_TRUE(streq16(s, u"ÿⱿ "));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstrn8_to_16("¶¶", 3));
|
||||
assert_se(streq16(s, u"¶"));
|
||||
ASSERT_TRUE(streq16(s, u"¶"));
|
||||
free(s);
|
||||
}
|
||||
|
||||
TEST(xstrn16_to_ascii) {
|
||||
char *s;
|
||||
|
||||
assert_se(s = xstrn16_to_ascii(NULL, 0));
|
||||
ASSERT_TRUE(streq8(s, ""));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstrn16_to_ascii(u"", 0));
|
||||
ASSERT_TRUE(streq8(s, ""));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstrn16_to_ascii(u"a", 0));
|
||||
ASSERT_TRUE(streq8(s, ""));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstrn16_to_ascii(u"", 1));
|
||||
ASSERT_TRUE(streq8(s, ""));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstrn16_to_ascii(u"1", 1));
|
||||
ASSERT_TRUE(streq8(s, "1"));
|
||||
free(s);
|
||||
|
||||
assert_se(s = xstr16_to_ascii(u"abcxyzABCXYZ09 .,-_#*!\"$%&/()=?`~"));
|
||||
ASSERT_TRUE(streq8(s, "abcxyzABCXYZ09 .,-_#*!\"$%&/()=?`~"));
|
||||
free(s);
|
||||
|
||||
assert_se(!xstr16_to_ascii(u"ÿⱿ𝇉 😺"));
|
||||
assert_se(!xstr16_to_ascii(u"¶¶"));
|
||||
}
|
||||
|
||||
TEST(startswith8) {
|
||||
assert_se(streq8(startswith8("", ""), ""));
|
||||
assert_se(streq8(startswith8("x", ""), "x"));
|
||||
@@ -791,4 +831,27 @@ TEST(efi_memset) {
|
||||
assert_se(memcmp(buf, "aaaaaaaaaa", 10) == 0);
|
||||
}
|
||||
|
||||
TEST(efi_strspn) {
|
||||
ASSERT_EQ(strspn16(u"foobar", u"fo"), 3U);
|
||||
ASSERT_EQ(strspn16(u"foobar", u"ob"), 0U);
|
||||
ASSERT_EQ(strspn16(u"foobar", u"fxayzy"), 1U);
|
||||
ASSERT_EQ(strspn16(u"", u"abcdefef"), 0U);
|
||||
ASSERT_EQ(strspn16(u"", u""), 0U);
|
||||
ASSERT_EQ(strspn16(u"foobar", u""), 0U);
|
||||
ASSERT_EQ(strspn16(u"foffffffffffoobar", u"fofo"), 14U);
|
||||
}
|
||||
|
||||
TEST(efi_strcspn) {
|
||||
ASSERT_EQ(strcspn16(u"foobar", u"fo"), 0U);
|
||||
ASSERT_EQ(strcspn16(u"foobar", u"bar"), 3U);
|
||||
ASSERT_EQ(strcspn16(u"foobar", u"xhyfzy"), 0U);
|
||||
ASSERT_EQ(strcspn16(u"foobar", u"xhyozy"), 1U);
|
||||
ASSERT_EQ(strcspn16(u"foobar", u"xhyzy"), 6U);
|
||||
ASSERT_EQ(strcspn16(u"", u"abcdefef"), 0U);
|
||||
ASSERT_EQ(strcspn16(u"", u""), 0U);
|
||||
ASSERT_EQ(strcspn16(u"foobar", u""), 6U);
|
||||
ASSERT_EQ(strcspn16(u"foffffffffffoobar", u"fofo"), 0U);
|
||||
ASSERT_EQ(strcspn16(u"foffffffffffoobar", u"a"), 15U);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "device-path-util.h"
|
||||
#include "efi-string.h"
|
||||
#include "proto/device-path.h"
|
||||
#include "url-discovery.h"
|
||||
|
||||
char16_t *disk_get_url(EFI_HANDLE *handle) {
|
||||
EFI_STATUS err;
|
||||
EFI_DEVICE_PATH *dp;
|
||||
|
||||
/* export the device path this image is started from */
|
||||
|
||||
if (!handle)
|
||||
return NULL;
|
||||
|
||||
err = BS->HandleProtocol(handle, MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL), (void **) &dp);
|
||||
if (err != EFI_SUCCESS)
|
||||
return NULL;
|
||||
|
||||
for (; !device_path_is_end(dp); dp = device_path_next_node(dp)) {
|
||||
if (dp->Type != MESSAGING_DEVICE_PATH || dp->SubType != MSG_URI_DP)
|
||||
continue;
|
||||
|
||||
URI_DEVICE_PATH *udp = (URI_DEVICE_PATH*) dp;
|
||||
return xstrn8_to_16(udp->Uri, dp->Length);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
char16_t *disk_get_url(EFI_HANDLE *handle);
|
||||
+39
-1
@@ -1,13 +1,14 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "device-path-util.h"
|
||||
#include "efi-string.h"
|
||||
#include "efivars.h"
|
||||
#include "memory-util-fundamental.h"
|
||||
#include "proto/device-path.h"
|
||||
#include "proto/simple-text-io.h"
|
||||
#include "ticks.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
#include "efivars.h"
|
||||
|
||||
/* Never try to read more than 16G into memory (and on 32bit 1G) */
|
||||
#define FILE_READ_MAX MIN(SIZE_MAX/4, UINT64_C(16)*1024U*1024U*1024U)
|
||||
@@ -511,3 +512,40 @@ bool free_and_xstrdup16(char16_t **p, const char16_t *s) {
|
||||
*p = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
char16_t *url_replace_last_component(const char16_t *url, const char16_t *filename) {
|
||||
assert(url);
|
||||
assert(filename);
|
||||
|
||||
/* Find colon separating protocol and hostname */
|
||||
const char16_t *d = strchr16(url, ':');
|
||||
if (!d || url == d)
|
||||
return NULL;
|
||||
d++;
|
||||
|
||||
/* Skip slashes after colon */
|
||||
d += strspn(d, u"/");
|
||||
|
||||
/* Skip everything till next slash or end (i.e. the hostname) */
|
||||
size_t n = strcspn(d, u"/?#");
|
||||
if (n == 0)
|
||||
return NULL;
|
||||
|
||||
d += n;
|
||||
|
||||
const char16_t *e = d + strcspn(d, u"?#"); /* Cut off "Query" and "Fragment" */
|
||||
|
||||
while (e > d && e[-1] == '/') /* Eat trailing slashes */
|
||||
e--;
|
||||
|
||||
const char16_t *p = e;
|
||||
while (p > d && p[-1] != '/') /* Find component before that */
|
||||
p--;
|
||||
|
||||
if (e <= p)
|
||||
return NULL;
|
||||
|
||||
_cleanup_free_ char16_t *chopped = xstrndup16(url, p - url);
|
||||
|
||||
return xasprintf("%ls/%ls", chopped, filename);
|
||||
}
|
||||
|
||||
@@ -251,3 +251,5 @@ char16_t *get_extra_dir(const EFI_DEVICE_PATH *file_path);
|
||||
#define xnew0(type, n) ASSERT_PTR(new0(type, n))
|
||||
|
||||
#endif
|
||||
|
||||
char16_t *url_replace_last_component(const char16_t *url, const char16_t *filename);
|
||||
|
||||
@@ -390,12 +390,17 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
{ EFI_LOADER_FEATURE_RETAIN_SHIM, "Retain SHIM protocols" },
|
||||
{ EFI_LOADER_FEATURE_MENU_DISABLE, "Menu can be disabled" },
|
||||
{ EFI_LOADER_FEATURE_MULTI_PROFILE_UKI, "Multi-Profile UKIs are supported" },
|
||||
{ EFI_LOADER_FEATURE_REPORT_URL, "Loader reports network boot URL" },
|
||||
{ EFI_LOADER_FEATURE_TYPE1_UKI, "Support Type #1 uki field" },
|
||||
{ EFI_LOADER_FEATURE_TYPE1_UKI_URL, "Support Type #1 uki-url field" },
|
||||
};
|
||||
static const struct {
|
||||
uint64_t flag;
|
||||
const char *name;
|
||||
} stub_flags[] = {
|
||||
{ EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub sets loader partition information" },
|
||||
{ EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub reports loader partition information" },
|
||||
{ EFI_STUB_FEATURE_REPORT_STUB_PARTITION, "Stub reports stub partition information" },
|
||||
{ EFI_STUB_FEATURE_REPORT_URL, "Stub reports network boot URL" },
|
||||
{ EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" },
|
||||
{ EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" },
|
||||
{ EFI_STUB_FEATURE_PICK_UP_CONFEXTS, "Picks up configuration extension images from boot partition" },
|
||||
@@ -405,7 +410,6 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
{ EFI_STUB_FEATURE_CMDLINE_SMBIOS, "Pick up .cmdline from SMBIOS Type 11" },
|
||||
{ EFI_STUB_FEATURE_DEVICETREE_ADDONS, "Pick up .dtb from addons" },
|
||||
{ EFI_STUB_FEATURE_MULTI_PROFILE_UKI, "Stub understands profile selector" },
|
||||
{ EFI_STUB_FEATURE_REPORT_STUB_PARTITION, "Stub sets stub partition information" },
|
||||
};
|
||||
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL, *stub_path = NULL,
|
||||
*current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL;
|
||||
@@ -480,6 +484,10 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
(void) efi_loader_get_device_part_uuid(&loader_partition_uuid);
|
||||
print_yes_no_line(/* first= */ false, !sd_id128_is_null(loader_partition_uuid), "Boot loader set partition information");
|
||||
|
||||
_cleanup_free_ char *loader_url = NULL;
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderDeviceURL"), &loader_url);
|
||||
print_yes_no_line(/* first= */ false, !!loader_url, "Boot loader set network boot URL information");
|
||||
|
||||
if (!sd_id128_is_null(loader_partition_uuid)) {
|
||||
if (!sd_id128_is_null(esp_uuid) && !sd_id128_equal(esp_uuid, loader_partition_uuid))
|
||||
printf("WARNING: The boot loader reports a different partition UUID than the detected ESP ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n",
|
||||
@@ -493,6 +501,9 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
if (loader_path)
|
||||
printf(" Loader: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
|
||||
|
||||
if (loader_url)
|
||||
printf(" Net Boot URL: %s\n", loader_url);
|
||||
|
||||
if (current_entry)
|
||||
printf("Current Entry: %s\n", current_entry);
|
||||
if (default_entry)
|
||||
@@ -513,6 +524,10 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
(void) efi_stub_get_device_part_uuid(&stub_partition_uuid);
|
||||
print_yes_no_line(/* first= */ false, !sd_id128_is_null(stub_partition_uuid), "Stub loader set partition information");
|
||||
|
||||
_cleanup_free_ char *stub_url = NULL;
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("StubDeviceURL"), &stub_url);
|
||||
print_yes_no_line(/* first= */ false, !!stub_url, "Stub set network boot URL information");
|
||||
|
||||
if (!sd_id128_is_null(stub_partition_uuid)) {
|
||||
if (!(!sd_id128_is_null(esp_uuid) && sd_id128_equal(esp_uuid, stub_partition_uuid)) &&
|
||||
!(!sd_id128_is_null(xbootldr_uuid) && sd_id128_equal(xbootldr_uuid, stub_partition_uuid)))
|
||||
@@ -526,6 +541,10 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
|
||||
if (stub_path)
|
||||
printf(" Stub: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(stub_path));
|
||||
|
||||
if (stub_url)
|
||||
printf(" Net Boot URL: %s\n", stub_url);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
+31
-4
@@ -94,12 +94,14 @@ static bool arg_rmdir = false;
|
||||
static bool arg_in_memory = false;
|
||||
static char **arg_argv = NULL;
|
||||
static char *arg_loop_ref = NULL;
|
||||
static bool arg_loop_ref_auto = false;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
static bool arg_mtree_hash = true;
|
||||
static bool arg_via_service = false;
|
||||
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_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
@@ -156,6 +158,7 @@ static int help(void) {
|
||||
" --json=pretty|short|off\n"
|
||||
" Generate JSON output\n"
|
||||
" --loop-ref=NAME Set reference string for loopback device\n"
|
||||
" --loop-ref-auto Derive reference string from image file name\n"
|
||||
" --mtree-hash=BOOL Whether to include SHA256 hash in the mtree output\n"
|
||||
" --user Discover user images\n"
|
||||
" --system Discover system images\n"
|
||||
@@ -179,6 +182,7 @@ static int help(void) {
|
||||
" --discover Discover DDIs in well known directories\n"
|
||||
" --validate Validate image and image policy\n"
|
||||
" --shift Shift UID range to selected base\n"
|
||||
" -q --quiet Suppress output of chosen loopback block device\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@@ -280,6 +284,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_ATTACH,
|
||||
ARG_DETACH,
|
||||
ARG_LOOP_REF,
|
||||
ARG_LOOP_REF_AUTO,
|
||||
ARG_IMAGE_POLICY,
|
||||
ARG_VALIDATE,
|
||||
ARG_MTREE_HASH,
|
||||
@@ -317,6 +322,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "discover", no_argument, NULL, ARG_DISCOVER },
|
||||
{ "loop-ref", required_argument, NULL, ARG_LOOP_REF },
|
||||
{ "loop-ref-auto", no_argument, NULL, ARG_LOOP_REF_AUTO },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{ "validate", no_argument, NULL, ARG_VALIDATE },
|
||||
{ "mtree-hash", required_argument, NULL, ARG_MTREE_HASH },
|
||||
@@ -325,6 +331,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{ "all", no_argument, NULL, ARG_ALL },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -339,7 +346,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hmurMUlxa", options, NULL)) >= 0) {
|
||||
while ((c = getopt_long(argc, argv, "hmurMUlxaq", options, NULL)) >= 0) {
|
||||
|
||||
switch (c) {
|
||||
|
||||
@@ -522,6 +529,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
case ARG_LOOP_REF:
|
||||
if (isempty(optarg)) {
|
||||
arg_loop_ref = mfree(arg_loop_ref);
|
||||
arg_loop_ref_auto = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -531,6 +539,13 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
r = free_and_strdup_warn(&arg_loop_ref, optarg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
arg_loop_ref_auto = false;
|
||||
break;
|
||||
|
||||
case ARG_LOOP_REF_AUTO:
|
||||
arg_loop_ref = mfree(arg_loop_ref);
|
||||
arg_loop_ref_auto = true;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY:
|
||||
@@ -573,6 +588,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_all = true;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -1979,7 +1998,9 @@ static int action_attach(DissectedImage *m, LoopDevice *d) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m");
|
||||
|
||||
puts(d->node);
|
||||
if (!arg_quiet)
|
||||
puts(d->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2149,6 +2170,12 @@ static int run(int argc, char *argv[]) {
|
||||
* support */
|
||||
}
|
||||
|
||||
if (!arg_loop_ref && arg_loop_ref_auto) {
|
||||
r = path_extract_filename(arg_image, &arg_loop_ref);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract file name from image path '%s': %m", arg_image);
|
||||
}
|
||||
|
||||
if (arg_action == ACTION_VALIDATE)
|
||||
return action_validate();
|
||||
|
||||
@@ -2214,8 +2241,8 @@ static int run(int argc, char *argv[]) {
|
||||
if (arg_in_memory)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--in-memory= not supported when operating via systemd-mountfsd.");
|
||||
|
||||
if (arg_loop_ref)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--loop-ref= not supported when operating via systemd-mountfsd.");
|
||||
if (arg_loop_ref || arg_loop_ref_auto) /* yes, the 2nd check is strictly speaking redundant, given the normalization we did above, but let's be explicit here */
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--loop-ref=/--loop-ref-auto not supported when operating via systemd-mountfsd.");
|
||||
|
||||
if (verity_settings_set(&arg_verity_settings))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Externally configured verity settings not supported when operating via systemd-mountfsd.");
|
||||
|
||||
@@ -494,7 +494,8 @@ static int add_mount(
|
||||
const char *opts,
|
||||
int passno,
|
||||
MountPointFlags flags,
|
||||
const char *target_unit) {
|
||||
const char *target_unit,
|
||||
const char *extra_after) {
|
||||
|
||||
_cleanup_free_ char *name = NULL, *automount_name = NULL, *filtered = NULL, *where_escaped = NULL,
|
||||
*opts_root_filtered = NULL;
|
||||
@@ -596,6 +597,9 @@ static int add_mount(
|
||||
if (target_unit && !FLAGS_SET(flags, MOUNT_NOFAIL))
|
||||
fprintf(f, "Before=%s\n", target_unit);
|
||||
|
||||
if (extra_after)
|
||||
fprintf(f, "After=%s\n", extra_after);
|
||||
|
||||
if (passno != 0) {
|
||||
r = generator_write_fsck_deps(f, dest, what, where, fstype);
|
||||
if (r < 0)
|
||||
@@ -814,15 +818,16 @@ static bool sysfs_check(void) {
|
||||
|
||||
static int add_sysusr_sysroot_usr_bind_mount(const char *source) {
|
||||
return add_mount(source,
|
||||
arg_dest,
|
||||
"/sysusr/usr",
|
||||
"/sysroot/usr",
|
||||
NULL,
|
||||
NULL,
|
||||
"bind",
|
||||
0,
|
||||
0,
|
||||
SPECIAL_INITRD_FS_TARGET);
|
||||
arg_dest,
|
||||
"/sysusr/usr",
|
||||
"/sysroot/usr",
|
||||
/* original_where= */ NULL,
|
||||
/* fstype= */ NULL,
|
||||
"bind",
|
||||
/* passno= */ 0,
|
||||
/* flags= */ 0,
|
||||
SPECIAL_INITRD_FS_TARGET,
|
||||
/* extra_after= */ NULL);
|
||||
}
|
||||
|
||||
static MountPointFlags fstab_options_to_flags(const char *options, bool is_swap) {
|
||||
@@ -1000,7 +1005,8 @@ static int parse_fstab_one(
|
||||
options,
|
||||
passno,
|
||||
flags,
|
||||
target_unit);
|
||||
target_unit,
|
||||
/* extra_after= */ NULL);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
@@ -1052,12 +1058,12 @@ static int parse_fstab(bool prefix_sysroot) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sysroot_is_nfsroot(void) {
|
||||
static int mount_source_is_nfsroot(const char *what) {
|
||||
union in_addr_union u;
|
||||
const char *sep, *a;
|
||||
int r;
|
||||
|
||||
assert(arg_root_what);
|
||||
assert(what);
|
||||
|
||||
/* From dracut.cmdline(7).
|
||||
*
|
||||
@@ -1070,18 +1076,18 @@ static int sysroot_is_nfsroot(void) {
|
||||
* If server-ip is an IPv6 address it has to be put in brackets, e.g. [2001:DB8::1]. NFS options
|
||||
* can be appended with the prefix ":" or "," and are separated by ",". */
|
||||
|
||||
if (path_equal(arg_root_what, "/dev/nfs") ||
|
||||
STR_IN_SET(arg_root_what, "dhcp", "dhcp6") ||
|
||||
STARTSWITH_SET(arg_root_what, "nfs:", "nfs4:"))
|
||||
if (path_equal(what, "/dev/nfs") ||
|
||||
STR_IN_SET(what, "dhcp", "dhcp6") ||
|
||||
STARTSWITH_SET(what, "nfs:", "nfs4:"))
|
||||
return true;
|
||||
|
||||
/* IPv6 address */
|
||||
if (arg_root_what[0] == '[') {
|
||||
sep = strchr(arg_root_what + 1, ']');
|
||||
if (what[0] == '[') {
|
||||
sep = strchr(what + 1, ']');
|
||||
if (!sep)
|
||||
return -EINVAL;
|
||||
|
||||
a = strndupa_safe(arg_root_what + 1, sep - arg_root_what - 1);
|
||||
a = strndupa_safe(what + 1, sep - what - 1);
|
||||
|
||||
r = in_addr_from_string(AF_INET6, a, &u);
|
||||
if (r < 0)
|
||||
@@ -1091,65 +1097,90 @@ static int sysroot_is_nfsroot(void) {
|
||||
}
|
||||
|
||||
/* IPv4 address */
|
||||
sep = strchr(arg_root_what, ':');
|
||||
sep = strchr(what, ':');
|
||||
if (sep) {
|
||||
a = strndupa_safe(arg_root_what, sep - arg_root_what);
|
||||
a = strndupa_safe(what, sep - what);
|
||||
|
||||
if (in_addr_from_string(AF_INET, a, &u) >= 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* root directory without address */
|
||||
return path_is_absolute(arg_root_what) && !path_startswith(arg_root_what, "/dev");
|
||||
return path_is_absolute(what) && !path_startswith(what, "/dev");
|
||||
}
|
||||
|
||||
static bool validate_root_or_usr_mount_source(const char *what, const char *switch_name) {
|
||||
int r;
|
||||
|
||||
assert(switch_name);
|
||||
|
||||
if (isempty(what)) {
|
||||
log_debug("No %s switch specified on the kernel command line.", switch_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (streq(what, "gpt-auto")) {
|
||||
/* This is handled by gpt-auto-generator */
|
||||
log_debug("Skipping %s directory handling, as gpt-auto was requested.", switch_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (streq(what, "fstab")) {
|
||||
/* This is handled by parse_fstab() */
|
||||
log_debug("Using initrd's fstab for %s configuration.", switch_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
r = mount_source_is_nfsroot(what);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to determine if the %s directory is on NFS, assuming not: %m", switch_name);
|
||||
else if (r > 0) {
|
||||
/* This is handled by the kernel or the initrd */
|
||||
log_debug("Skipping %s directory handling, as root on NFS was requested.", switch_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (startswith(what, "cifs://")) {
|
||||
log_debug("Skipping %s directory handling, as root on CIFS was requested.", switch_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (startswith(what, "iscsi:")) {
|
||||
log_debug("Skipping %s directory handling, as root on iSCSI was requested.", switch_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (startswith(what, "live:")) {
|
||||
log_debug("Skipping %s directory handling, as root on live image was requested.", switch_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int add_sysroot_mount(void) {
|
||||
_cleanup_free_ char *what = NULL;
|
||||
const char *opts, *fstype;
|
||||
bool default_rw, makefs;
|
||||
const char *extra_opts = NULL, *fstype = NULL;
|
||||
bool default_rw = true, makefs = false;
|
||||
MountPointFlags flags;
|
||||
int r;
|
||||
|
||||
if (isempty(arg_root_what)) {
|
||||
log_debug("Could not find a root= entry on the kernel command line.");
|
||||
if (!validate_root_or_usr_mount_source(arg_root_what, "root="))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (streq(arg_root_what, "gpt-auto")) {
|
||||
/* This is handled by gpt-auto-generator */
|
||||
log_debug("Skipping root directory handling, as gpt-auto was requested.");
|
||||
return 0;
|
||||
} else if (streq(arg_root_what, "fstab")) {
|
||||
/* This is handled by parse_fstab */
|
||||
log_debug("Using initrd's fstab for /sysroot/ configuration.");
|
||||
return 0;
|
||||
}
|
||||
const char *bind = startswith(arg_root_what, "bind:");
|
||||
if (bind) {
|
||||
if (!path_is_valid(bind) || !path_is_absolute(bind)) {
|
||||
log_debug("Invalid root=bind: source path, ignoring: %s", bind);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sysroot_is_nfsroot();
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to determine if the root directory is on NFS, assuming not: %m");
|
||||
else if (r > 0) {
|
||||
/* This is handled by the kernel or the initrd */
|
||||
log_debug("Skipping root directory handling, as root on NFS was requested.");
|
||||
return 0;
|
||||
}
|
||||
what = strdup(bind);
|
||||
if (!what)
|
||||
return log_oom();
|
||||
|
||||
if (startswith(arg_root_what, "cifs://")) {
|
||||
log_debug("Skipping root directory handling, as root on CIFS was requested.");
|
||||
return 0;
|
||||
}
|
||||
extra_opts = "bind";
|
||||
|
||||
if (startswith(arg_root_what, "iscsi:")) {
|
||||
log_debug("Skipping root directory handling, as root on iSCSI was requested.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (startswith(arg_root_what, "live:")) {
|
||||
log_debug("Skipping root directory handling, as root on live image was requested.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (streq(arg_root_what, "tmpfs")) {
|
||||
} else if (streq(arg_root_what, "tmpfs")) {
|
||||
/* If root=tmpfs is specified, then take this as shortcut for a writable tmpfs mount as root */
|
||||
|
||||
what = strdup("rootfs"); /* just a pretty name, to show up in /proc/self/mountinfo */
|
||||
@@ -1157,8 +1188,6 @@ static int add_sysroot_mount(void) {
|
||||
return log_oom();
|
||||
|
||||
fstype = arg_root_fstype ?: "tmpfs"; /* tmpfs, unless overridden */
|
||||
|
||||
default_rw = true; /* writable, unless overridden */;
|
||||
} else {
|
||||
|
||||
what = fstab_node_to_udev_node(arg_root_what);
|
||||
@@ -1166,40 +1195,44 @@ static int add_sysroot_mount(void) {
|
||||
return log_oom();
|
||||
|
||||
fstype = arg_root_fstype; /* if not specified explicitly, don't default to anything here */
|
||||
|
||||
default_rw = false; /* read-only, unless overridden */
|
||||
}
|
||||
|
||||
if (!arg_root_options)
|
||||
opts = arg_root_rw > 0 || (arg_root_rw < 0 && default_rw) ? "rw" : "ro";
|
||||
else if (arg_root_rw >= 0 ||
|
||||
!fstab_test_option(arg_root_options, "ro\0" "rw\0"))
|
||||
opts = strjoina(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
|
||||
else
|
||||
opts = arg_root_options;
|
||||
_cleanup_free_ char *combined_options = NULL;
|
||||
if (strdup_to(&combined_options, arg_root_options) < 0)
|
||||
return log_oom();
|
||||
|
||||
log_debug("Found entry what=%s where=/sysroot type=%s opts=%s", what, strna(arg_root_fstype), strempty(opts));
|
||||
if (arg_root_rw >= 0 || !fstab_test_option(combined_options, "ro\0" "rw\0"))
|
||||
if (!strextend_with_separator(&combined_options, ",", arg_root_rw > 0 || (arg_root_rw < 0 && default_rw) ? "rw" : "ro"))
|
||||
return log_oom();
|
||||
|
||||
makefs = fstab_test_option(opts, "x-systemd.makefs\0");
|
||||
if (extra_opts)
|
||||
if (!strextend_with_separator(&combined_options, ",", extra_opts))
|
||||
return log_oom();
|
||||
|
||||
log_debug("Found entry what=%s where=/sysroot type=%s opts=%s", what, strna(arg_root_fstype), strempty(combined_options));
|
||||
|
||||
makefs = fstab_test_option(combined_options, "x-systemd.makefs\0");
|
||||
flags = makefs * MOUNT_MAKEFS;
|
||||
|
||||
return add_mount("/proc/cmdline",
|
||||
arg_dest,
|
||||
what,
|
||||
"/sysroot",
|
||||
NULL,
|
||||
/* original_where= */ NULL,
|
||||
fstype,
|
||||
opts,
|
||||
is_device_path(what) ? 1 : 0, /* passno */
|
||||
flags, /* makefs off, pcrfs off, quota off, noauto off, nofail off, automount off */
|
||||
SPECIAL_INITRD_ROOT_FS_TARGET);
|
||||
combined_options,
|
||||
/* passno= */ is_device_path(what) ? 1 : 0,
|
||||
flags,
|
||||
SPECIAL_INITRD_ROOT_FS_TARGET,
|
||||
"imports.target");
|
||||
}
|
||||
|
||||
static int add_sysroot_usr_mount(void) {
|
||||
_cleanup_free_ char *what = NULL;
|
||||
const char *opts;
|
||||
bool makefs;
|
||||
const char *extra_opts = NULL;
|
||||
MountPointFlags flags;
|
||||
bool makefs;
|
||||
int r;
|
||||
|
||||
/* Returns 0 if we didn't do anything, > 0 if we either generated a unit for the /usr/ mount, or we
|
||||
@@ -1227,39 +1260,38 @@ static int add_sysroot_usr_mount(void) {
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (isempty(arg_usr_what)) {
|
||||
log_debug("Could not find a mount.usr= entry on the kernel command line.");
|
||||
if (!validate_root_or_usr_mount_source(arg_usr_what, "mount.usr="))
|
||||
return 0;
|
||||
|
||||
const char *bind = startswith(arg_usr_what, "bind:");
|
||||
if (bind) {
|
||||
if (!path_is_valid(bind) || !path_is_absolute(bind)) {
|
||||
log_debug("Invalid mount.usr=bind: source path, ignoring: %s", bind);
|
||||
return 0;
|
||||
}
|
||||
|
||||
what = strdup(bind);
|
||||
if (!what)
|
||||
return log_oom();
|
||||
|
||||
extra_opts = "bind";
|
||||
} else {
|
||||
what = fstab_node_to_udev_node(arg_usr_what);
|
||||
if (!what)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (streq(arg_usr_what, "gpt-auto")) {
|
||||
/* This is handled by the gpt-auto generator */
|
||||
log_debug("Skipping /usr/ directory handling, as gpt-auto was requested.");
|
||||
return 1; /* systemd-gpt-auto-generator will generate a unit for this, hence report that a
|
||||
* unit file is being created for the host /usr/ mount. */
|
||||
} else if (streq(arg_usr_what, "fstab")) {
|
||||
/* This is handled by parse_fstab */
|
||||
log_debug("Using initrd's fstab for /sysroot/usr/ configuration.");
|
||||
return 1; /* parse_fstab will generate a unit for this, hence report that a
|
||||
* unit file is being created for the host /usr/ mount. */
|
||||
}
|
||||
|
||||
if (path_equal(arg_usr_what, "/dev/nfs")) {
|
||||
/* This is handled by the initrd (if at all supported, that is) */
|
||||
log_debug("Skipping /usr/ directory handling, as /dev/nfs was requested.");
|
||||
return 1; /* As above, report that NFS code will create the unit */
|
||||
}
|
||||
|
||||
what = fstab_node_to_udev_node(arg_usr_what);
|
||||
if (!what)
|
||||
_cleanup_free_ char *combined_options = NULL;
|
||||
if (strdup_to(&combined_options, arg_usr_options) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (!arg_usr_options)
|
||||
opts = arg_root_rw > 0 ? "rw" : "ro";
|
||||
else if (!fstab_test_option(arg_usr_options, "ro\0" "rw\0"))
|
||||
opts = strjoina(arg_usr_options, ",", arg_root_rw > 0 ? "rw" : "ro");
|
||||
else
|
||||
opts = arg_usr_options;
|
||||
if (!fstab_test_option(combined_options, "ro\0" "rw\0"))
|
||||
if (!strextend_with_separator(&combined_options, ",", arg_root_rw > 0 ? "rw" : "ro"))
|
||||
return log_oom();
|
||||
|
||||
if (extra_opts)
|
||||
if (!strextend_with_separator(&combined_options, ",", extra_opts))
|
||||
return log_oom();
|
||||
|
||||
/* When mounting /usr from the initrd, we add an extra level of indirection: we first mount the /usr/
|
||||
* partition to /sysusr/usr/, and then afterwards bind mount that to /sysroot/usr/. We do this so
|
||||
@@ -1268,21 +1300,22 @@ static int add_sysroot_usr_mount(void) {
|
||||
* this should order itself after initrd-usr-fs.target and before initrd-fs.target; and it should
|
||||
* look into both /sysusr/ and /sysroot/ for the configuration data to apply. */
|
||||
|
||||
log_debug("Found entry what=%s where=/sysusr/usr type=%s opts=%s", what, strna(arg_usr_fstype), strempty(opts));
|
||||
log_debug("Found entry what=%s where=/sysusr/usr type=%s opts=%s", what, strna(arg_usr_fstype), strempty(combined_options));
|
||||
|
||||
makefs = fstab_test_option(opts, "x-systemd.makefs\0");
|
||||
makefs = fstab_test_option(combined_options, "x-systemd.makefs\0");
|
||||
flags = makefs * MOUNT_MAKEFS;
|
||||
|
||||
r = add_mount("/proc/cmdline",
|
||||
arg_dest,
|
||||
what,
|
||||
"/sysusr/usr",
|
||||
NULL,
|
||||
/* original_where= */ NULL,
|
||||
arg_usr_fstype,
|
||||
opts,
|
||||
is_device_path(what) ? 1 : 0, /* passno */
|
||||
combined_options,
|
||||
/* passno= */ is_device_path(what) ? 1 : 0,
|
||||
flags,
|
||||
SPECIAL_INITRD_USR_FS_TARGET);
|
||||
SPECIAL_INITRD_USR_FS_TARGET,
|
||||
"imports.target");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1336,12 +1369,13 @@ static int add_volatile_var(void) {
|
||||
arg_dest_late,
|
||||
"tmpfs",
|
||||
"/var",
|
||||
NULL,
|
||||
/* original_where= */ NULL,
|
||||
"tmpfs",
|
||||
"mode=0755" TMPFS_LIMITS_VAR,
|
||||
0,
|
||||
0,
|
||||
SPECIAL_LOCAL_FS_TARGET);
|
||||
/* passno= */ 0,
|
||||
/* flags= */ 0,
|
||||
SPECIAL_LOCAL_FS_TARGET,
|
||||
/* extra_after= */ NULL);
|
||||
}
|
||||
|
||||
static int add_mounts_from_cmdline(void) {
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#define EFI_LOADER_FEATURE_RETAIN_SHIM (UINT64_C(1) << 12)
|
||||
#define EFI_LOADER_FEATURE_MENU_DISABLE (UINT64_C(1) << 13)
|
||||
#define EFI_LOADER_FEATURE_MULTI_PROFILE_UKI (UINT64_C(1) << 14)
|
||||
#define EFI_LOADER_FEATURE_REPORT_URL (UINT64_C(1) << 15)
|
||||
#define EFI_LOADER_FEATURE_TYPE1_UKI (UINT64_C(1) << 16)
|
||||
#define EFI_LOADER_FEATURE_TYPE1_UKI_URL (UINT64_C(1) << 17)
|
||||
|
||||
/* Features of the stub, i.e. systemd-stub */
|
||||
#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0)
|
||||
@@ -37,6 +40,7 @@
|
||||
#define EFI_STUB_FEATURE_PICK_UP_CONFEXTS (UINT64_C(1) << 8)
|
||||
#define EFI_STUB_FEATURE_MULTI_PROFILE_UKI (UINT64_C(1) << 9)
|
||||
#define EFI_STUB_FEATURE_REPORT_STUB_PARTITION (UINT64_C(1) << 10)
|
||||
#define EFI_STUB_FEATURE_REPORT_URL (UINT64_C(1) << 11)
|
||||
|
||||
typedef enum SecureBootMode {
|
||||
SECURE_BOOT_UNSUPPORTED,
|
||||
|
||||
@@ -8,7 +8,9 @@ struct iovec {
|
||||
size_t iov_len;
|
||||
};
|
||||
|
||||
DISABLE_WARNING_REDUNDANT_DECLS;
|
||||
static inline void free(void *p);
|
||||
REENABLE_WARNING;
|
||||
#endif
|
||||
|
||||
/* This accepts both const and non-const pointers */
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
_Pragma("GCC diagnostic push"); \
|
||||
_Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"")
|
||||
|
||||
#define DISABLE_WARNING_REDUNDANT_DECLS \
|
||||
_Pragma("GCC diagnostic push"); \
|
||||
_Pragma("GCC diagnostic ignored \"-Wredundant-decls\"")
|
||||
|
||||
#if HAVE_WARNING_ZERO_LENGTH_BOUNDS
|
||||
# define DISABLE_WARNING_ZERO_LENGTH_BOUNDS \
|
||||
_Pragma("GCC diagnostic push"); \
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
# define strncmp strncmp16
|
||||
# define strcasecmp strcasecmp16
|
||||
# define strncasecmp strncasecmp16
|
||||
# define strspn strspn16
|
||||
# define strcspn strcspn16
|
||||
# define STR_C(str) (L ## str)
|
||||
typedef char16_t sd_char;
|
||||
#else
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
static const char *arg_dest = NULL;
|
||||
static bool arg_enabled = true;
|
||||
static bool arg_root_enabled = true;
|
||||
static int arg_root_enabled = -1; /* tristate */
|
||||
static bool arg_swap_enabled = true;
|
||||
static char *arg_root_fstype = NULL;
|
||||
static char *arg_root_options = NULL;
|
||||
@@ -673,21 +673,29 @@ static int add_root_mount(void) {
|
||||
_cleanup_free_ char *options = NULL;
|
||||
int r;
|
||||
|
||||
if (!is_efi_boot()) {
|
||||
log_debug("Not an EFI boot, not creating root mount.");
|
||||
/* Explicitly disabled? Then exit immediately */
|
||||
if (arg_root_enabled == 0)
|
||||
return 0;
|
||||
|
||||
/* Neither explicitly enabled nor disabled? Then decide based on the EFI partition variables to be set */
|
||||
if (arg_root_enabled < 0) {
|
||||
if (!is_efi_boot()) {
|
||||
log_debug("Not an EFI boot, not creating root mount.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_loader_get_device_part_uuid(/* ret_uuid= */ NULL);
|
||||
if (r == -ENOENT) {
|
||||
log_notice("EFI loader partition unknown, exiting.\n"
|
||||
"(The boot loader did not set EFI variable LoaderDevicePartUUID.)");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read loader partition UUID: %m");
|
||||
}
|
||||
|
||||
r = efi_loader_get_device_part_uuid(NULL);
|
||||
if (r == -ENOENT) {
|
||||
log_notice("EFI loader partition unknown, exiting.\n"
|
||||
"(The boot loader did not set EFI variable LoaderDevicePartUUID.)");
|
||||
return 0;
|
||||
} else if (r < 0)
|
||||
return log_error_errno(r, "Failed to read loader partition UUID: %m");
|
||||
|
||||
/* OK, we have an ESP/XBOOTLDR partition, this is fantastic, so let's wait for a root device to show up.
|
||||
* A udev rule will create the link for us under the right name. */
|
||||
/* OK, we shall look for a root device, so let's wait for a root device to show up. A udev rule will
|
||||
* create the link for us under the right name. */
|
||||
|
||||
if (in_initrd()) {
|
||||
r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
|
||||
@@ -904,9 +912,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
||||
/* Disable root disk logic if there's a root= value
|
||||
* specified (unless it happens to be "gpt-auto") */
|
||||
|
||||
if (!streq(value, "gpt-auto")) {
|
||||
if (streq(value, "gpt-auto")) {
|
||||
arg_root_enabled = true;
|
||||
log_debug("Enabling root partition auto-detection, root= is explicitly set to 'gpt_auto'.");
|
||||
} else {
|
||||
arg_root_enabled = false;
|
||||
log_debug("Disabling root partition auto-detection, root= is defined.");
|
||||
log_debug("Disabling root partition auto-detection, root= is neither unset, nor set to 'gpt-auto'.");
|
||||
}
|
||||
|
||||
} else if (streq(key, "roothash")) {
|
||||
@@ -917,6 +928,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
||||
/* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
|
||||
|
||||
arg_root_enabled = false;
|
||||
log_debug("Disabling root partition auto-detection, roothash= is set.");
|
||||
|
||||
} else if (streq(key, "rootfstype")) {
|
||||
|
||||
@@ -975,10 +987,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg_root_enabled)
|
||||
r = add_root_mount();
|
||||
else
|
||||
r = 0;
|
||||
r = add_root_mount();
|
||||
|
||||
if (!in_initrd())
|
||||
RET_GATHER(r, add_mounts());
|
||||
|
||||
+168
-28
@@ -4,24 +4,49 @@
|
||||
|
||||
#include "creds-util.h"
|
||||
#include "discover-image.h"
|
||||
#include "efivars.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "generator.h"
|
||||
#include "import-util.h"
|
||||
#include "initrd-util.h"
|
||||
#include "json-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "special.h"
|
||||
#include "specifier.h"
|
||||
#include "unit-name.h"
|
||||
#include "web-util.h"
|
||||
|
||||
typedef struct Transfer {
|
||||
ImageClass class;
|
||||
ImportType type;
|
||||
char *local;
|
||||
char *remote;
|
||||
const char *image_root;
|
||||
bool blockdev;
|
||||
sd_json_variant *json;
|
||||
} Transfer;
|
||||
|
||||
static const char *arg_dest = NULL;
|
||||
static char *arg_success_action = NULL;
|
||||
static char *arg_failure_action = NULL;
|
||||
static sd_json_variant **arg_transfers = NULL;
|
||||
static Transfer *arg_transfers = NULL;
|
||||
static size_t arg_n_transfers = 0;
|
||||
|
||||
static void transfer_destroy_many(Transfer *transfers, size_t n) {
|
||||
FOREACH_ARRAY(t, transfers, n) {
|
||||
free(t->local);
|
||||
free(t->remote);
|
||||
sd_json_variant_unref(t->json);
|
||||
}
|
||||
|
||||
free(transfers);
|
||||
}
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_success_action, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_failure_action, freep);
|
||||
STATIC_ARRAY_DESTRUCTOR_REGISTER(arg_transfers, arg_n_transfers, sd_json_variant_unref_many);
|
||||
STATIC_ARRAY_DESTRUCTOR_REGISTER(arg_transfers, arg_n_transfers, transfer_destroy_many);
|
||||
|
||||
static int parse_pull_expression(const char *v) {
|
||||
const char *p = v;
|
||||
@@ -43,8 +68,6 @@ static int parse_pull_expression(const char *v) {
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No local string in pull expression '%s': %m", v);
|
||||
|
||||
if (!http_url_is_valid(p) && !file_url_is_valid(p))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid URL, refusing: %s", p);
|
||||
_cleanup_free_ char *remote = strdup(p);
|
||||
if (!remote)
|
||||
return log_oom();
|
||||
@@ -57,7 +80,7 @@ static int parse_pull_expression(const char *v) {
|
||||
ImportType type = _IMPORT_TYPE_INVALID;
|
||||
ImageClass class = _IMAGE_CLASS_INVALID;
|
||||
ImportVerify verify = IMPORT_VERIFY_SIGNATURE;
|
||||
bool ro = false;
|
||||
bool ro = false, blockdev = false, bootorigin = false, runtime = in_initrd();
|
||||
|
||||
const char *o = options;
|
||||
for (;;) {
|
||||
@@ -75,8 +98,17 @@ static int parse_pull_expression(const char *v) {
|
||||
ro = true;
|
||||
else if (streq(opt, "rw"))
|
||||
ro = false;
|
||||
else if ((suffix = startswith(opt, "verify="))) {
|
||||
|
||||
else if (streq(opt, "blockdev"))
|
||||
blockdev = true;
|
||||
else if (streq(opt, "bootorigin"))
|
||||
bootorigin = true;
|
||||
else if ((suffix = startswith(opt, "runtime="))) {
|
||||
r = parse_boolean(suffix);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Unknown runtime= parameter, ignoring: %s", suffix);
|
||||
else
|
||||
runtime = r;
|
||||
} else if ((suffix = startswith(opt, "verify="))) {
|
||||
ImportVerify w = import_verify_from_string(suffix);
|
||||
|
||||
if (w < 0)
|
||||
@@ -100,28 +132,94 @@ static int parse_pull_expression(const char *v) {
|
||||
}
|
||||
}
|
||||
|
||||
if (bootorigin) {
|
||||
_cleanup_free_ char *stub_url = NULL;
|
||||
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("StubDeviceURL"), &stub_url);
|
||||
if (r == -ENOENT) {
|
||||
log_debug("Option 'bootorigin' specified, but StubDeviceURL EFI variable not set, not scheduling import job for '%s'.", remote);
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read 'StubDeviceURL' EFI variable: %m");
|
||||
|
||||
if (!http_url_is_valid(stub_url))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Value of 'StubDeviceURL' is not a valid URL, refusing: %s", stub_url);
|
||||
|
||||
_cleanup_free_ char *result = NULL;
|
||||
r = import_url_change_last_component(stub_url, remote, &result);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to replace last component of URL '%s': %m", stub_url);
|
||||
|
||||
log_info("URL reported by StubDeviceURL is '%s', derived download URL '%s' from it.", stub_url, result);
|
||||
free_and_replace(remote, result);
|
||||
}
|
||||
|
||||
if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid URL, refusing: %s", remote);
|
||||
|
||||
if (type < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No image type (raw, tar) specified in pull expression, refusing: %s", v);
|
||||
if (class < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No image class (machine, portable, sysext, confext) specified in pull expression, refusing: %s", v);
|
||||
|
||||
if (!local) {
|
||||
_cleanup_free_ char *c = NULL;
|
||||
r = import_url_last_component(remote, &c);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate local name from URL '%s': %m", remote);
|
||||
|
||||
switch (type) {
|
||||
|
||||
case IMPORT_RAW:
|
||||
r = raw_strip_suffixes(c, &local);
|
||||
break;
|
||||
|
||||
case IMPORT_TAR:
|
||||
r = tar_strip_suffixes(c, &local);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
break;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to strip suffix from URL '%s': %m", remote);
|
||||
|
||||
log_info("Saving downloaded file under local name '%s'.", local);
|
||||
}
|
||||
|
||||
if (blockdev && type != IMPORT_RAW)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option 'blockdev' only available for raw images, refusing: %s", v);
|
||||
|
||||
if (!GREEDY_REALLOC(arg_transfers, arg_n_transfers + 1))
|
||||
return log_oom();
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
|
||||
const char *image_root = runtime ? image_root_runtime_to_string(class) : image_root_to_string(class);
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
|
||||
r = sd_json_buildo(
|
||||
&j,
|
||||
SD_JSON_BUILD_PAIR("remote", SD_JSON_BUILD_STRING(remote)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!!local, "local", SD_JSON_BUILD_STRING(local)),
|
||||
SD_JSON_BUILD_PAIR("local", SD_JSON_BUILD_STRING(local)),
|
||||
SD_JSON_BUILD_PAIR("class", JSON_BUILD_STRING_UNDERSCORIFY(image_class_to_string(class))),
|
||||
SD_JSON_BUILD_PAIR("type", JSON_BUILD_STRING_UNDERSCORIFY(import_type_to_string(type))),
|
||||
SD_JSON_BUILD_PAIR("readOnly", SD_JSON_BUILD_BOOLEAN(ro)),
|
||||
SD_JSON_BUILD_PAIR("verify", JSON_BUILD_STRING_UNDERSCORIFY(import_verify_to_string(verify))));
|
||||
SD_JSON_BUILD_PAIR("verify", JSON_BUILD_STRING_UNDERSCORIFY(import_verify_to_string(verify))),
|
||||
SD_JSON_BUILD_PAIR("imageRoot", SD_JSON_BUILD_STRING(image_root)));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build import JSON object: %m");
|
||||
|
||||
arg_transfers[arg_n_transfers++] = TAKE_PTR(j);
|
||||
arg_transfers[arg_n_transfers++] = (Transfer) {
|
||||
.class = class,
|
||||
.type = type,
|
||||
.local = TAKE_PTR(local),
|
||||
.remote = TAKE_PTR(remote),
|
||||
.image_root = image_root,
|
||||
.json = TAKE_PTR(j),
|
||||
.blockdev = blockdev,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -191,33 +289,55 @@ static int parse_credentials(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int transfer_generate(sd_json_variant *v, size_t c) {
|
||||
static char *transfer_get_local_path(const Transfer *t) {
|
||||
assert(t);
|
||||
assert(t->image_root);
|
||||
assert(t->local);
|
||||
|
||||
switch (t->type) {
|
||||
case IMPORT_RAW:
|
||||
return strjoin(t->image_root, "/", t->local, ".raw");
|
||||
|
||||
case IMPORT_TAR:
|
||||
return path_join(t->image_root, t->local);
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static int transfer_generate(const Transfer *t) {
|
||||
int r;
|
||||
|
||||
assert(v);
|
||||
assert(t);
|
||||
|
||||
_cleanup_free_ char *service = NULL;
|
||||
if (asprintf(&service, "import%zu.service", c) < 0)
|
||||
_cleanup_free_ char *local_path = transfer_get_local_path(t);
|
||||
if (!local_path)
|
||||
return log_oom();
|
||||
|
||||
/* Give this unit a clear name derived from the file system object we are installed into the OS, so
|
||||
* that other components can nicely have dependencies on this. */
|
||||
_cleanup_free_ char *service = NULL;
|
||||
r = unit_name_from_path_instance("systemd-import", local_path, ".service", &service);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build import unit name from '%s': %m", local_path);
|
||||
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
r = generator_open_unit_file(arg_dest, /* source = */ NULL, service, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
const char *remote = sd_json_variant_string(sd_json_variant_by_key(v, "remote"));
|
||||
|
||||
fprintf(f,
|
||||
"[Unit]\n"
|
||||
"Description=Download of %s\n"
|
||||
"Documentation=man:systemd-import-generator(8)\n"
|
||||
"SourcePath=/proc/cmdline\n"
|
||||
"Requires=systemd-importd.socket\n"
|
||||
"After=systemd-importd.socket\n"
|
||||
"After=imports-pre.target systemd-importd.socket\n"
|
||||
"Conflicts=shutdown.target\n"
|
||||
"Before=shutdown.target\n"
|
||||
"Before=imports.target shutdown.target\n"
|
||||
"DefaultDependencies=no\n",
|
||||
remote);
|
||||
t->remote);
|
||||
|
||||
if (arg_success_action)
|
||||
fprintf(f, "SuccessAction=%s\n",
|
||||
@@ -227,24 +347,35 @@ static int transfer_generate(sd_json_variant *v, size_t c) {
|
||||
fprintf(f, "FailureAction=%s\n",
|
||||
arg_failure_action);
|
||||
|
||||
const char *class = sd_json_variant_string(sd_json_variant_by_key(v, "class"));
|
||||
if (streq_ptr(class, "sysext"))
|
||||
if (t->class == IMAGE_SYSEXT)
|
||||
fputs("Before=systemd-sysext.service\n", f);
|
||||
else if (streq_ptr(class, "confext"))
|
||||
else if (t->class == IMAGE_CONFEXT)
|
||||
fputs("Before=systemd-confext.service\n", f);
|
||||
|
||||
/* Assume network resource unless URL is file:// */
|
||||
if (!file_url_is_valid(remote))
|
||||
if (!file_url_is_valid(t->remote))
|
||||
fputs("Wants=network-online.target\n"
|
||||
"After=network-online.target\n", f);
|
||||
|
||||
_cleanup_free_ char *loop_service = NULL;
|
||||
if (t->blockdev) {
|
||||
assert(t->type == IMPORT_RAW);
|
||||
|
||||
r = unit_name_from_path_instance("systemd-loop", local_path, ".service", &loop_service);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build systemd-loop@.service instance name from path '%s': %m", local_path);
|
||||
|
||||
/* Make sure download completes before the loopback service is activated */
|
||||
fprintf(f, "Before=%s\n", loop_service);
|
||||
}
|
||||
|
||||
fputs("\n"
|
||||
"[Service]\n"
|
||||
"Type=oneshot\n"
|
||||
"NotifyAccess=main\n", f);
|
||||
|
||||
_cleanup_free_ char *formatted = NULL;
|
||||
r = sd_json_variant_format(v, /* flags= */ 0, &formatted);
|
||||
r = sd_json_variant_format(t->json, /* flags= */ 0, &formatted);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to format import JSON data: %m");
|
||||
|
||||
@@ -259,15 +390,24 @@ static int transfer_generate(sd_json_variant *v, size_t c) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write unit %s: %m", service);
|
||||
|
||||
return generator_add_symlink(arg_dest, "multi-user.target", "wants", service);
|
||||
r = generator_add_symlink(arg_dest, "imports.target", "wants", service);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (loop_service) {
|
||||
r = generator_add_symlink(arg_dest, "imports.target", "wants", loop_service);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate(void) {
|
||||
size_t c = 0;
|
||||
int r = 0;
|
||||
|
||||
FOREACH_ARRAY(i, arg_transfers, arg_n_transfers)
|
||||
RET_GATHER(r, transfer_generate(*i, c++));
|
||||
RET_GATHER(r, transfer_generate(i));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ struct Transfer {
|
||||
|
||||
char *remote;
|
||||
char *local;
|
||||
char *image_root;
|
||||
ImageClass class;
|
||||
ImportFlags flags;
|
||||
char *format;
|
||||
@@ -146,6 +147,7 @@ static Transfer *transfer_unref(Transfer *t) {
|
||||
free(t->remote);
|
||||
free(t->local);
|
||||
free(t->format);
|
||||
free(t->image_root);
|
||||
free(t->object_path);
|
||||
|
||||
pidref_done_sigkill_wait(&t->pidref);
|
||||
@@ -455,6 +457,8 @@ static int transfer_start(Transfer *t) {
|
||||
NULL, /* if so: the actual URL */
|
||||
NULL, /* maybe --format= */
|
||||
NULL, /* if so: the actual format */
|
||||
NULL, /* maybe --image-root= */
|
||||
NULL, /* if so: the image root path */
|
||||
NULL, /* remote */
|
||||
NULL, /* local */
|
||||
NULL
|
||||
@@ -548,6 +552,11 @@ static int transfer_start(Transfer *t) {
|
||||
cmd[k++] = t->format;
|
||||
}
|
||||
|
||||
if (t->image_root) {
|
||||
cmd[k++] = "--image-root";
|
||||
cmd[k++] = t->image_root;
|
||||
}
|
||||
|
||||
if (!IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) {
|
||||
if (t->remote)
|
||||
cmd[k++] = t->remote;
|
||||
@@ -1838,6 +1847,7 @@ static int vl_method_pull(sd_varlink *link, sd_json_variant *parameters, sd_varl
|
||||
bool force;
|
||||
bool read_only;
|
||||
bool keep_download;
|
||||
const char *image_root;
|
||||
} p = {
|
||||
.class = _IMAGE_CLASS_INVALID,
|
||||
.verify = IMPORT_VERIFY_SIGNATURE,
|
||||
@@ -1852,6 +1862,7 @@ static int vl_method_pull(sd_varlink *link, sd_json_variant *parameters, sd_varl
|
||||
{ "force", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct p, force), 0 },
|
||||
{ "readOnly", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct p, read_only), 0 },
|
||||
{ "keepDownload", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct p, keep_download), 0 },
|
||||
{ "imageRoot", SD_JSON_VARIANT_STRING, json_dispatch_const_path, offsetof(struct p, image_root), SD_JSON_STRICT },
|
||||
VARLINK_DISPATCH_POLKIT_FIELD,
|
||||
{},
|
||||
};
|
||||
@@ -1918,6 +1929,12 @@ static int vl_method_pull(sd_varlink *link, sd_json_variant *parameters, sd_varl
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (p.image_root) {
|
||||
t->image_root = strdup(p.image_root);
|
||||
if (!t->image_root)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = transfer_start(t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -110,6 +110,15 @@ static const char *const image_root_table[_IMAGE_CLASS_MAX] = {
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(image_root, ImageClass);
|
||||
|
||||
static const char *const image_root_runtime_table[_IMAGE_CLASS_MAX] = {
|
||||
[IMAGE_MACHINE] = "/run/machines",
|
||||
[IMAGE_PORTABLE] = "/run/portables",
|
||||
[IMAGE_SYSEXT] = "/run/extensions",
|
||||
[IMAGE_CONFEXT] = "/run/confexts",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(image_root_runtime, ImageClass);
|
||||
|
||||
static Image *image_free(Image *i) {
|
||||
assert(i);
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@ static inline bool IMAGE_IS_HOST(const struct Image *i) {
|
||||
int image_to_json(const struct Image *i, sd_json_variant **ret);
|
||||
|
||||
const char* image_root_to_string(ImageClass c) _const_;
|
||||
const char* image_root_runtime_to_string(ImageClass c) _const_;
|
||||
|
||||
extern const struct hash_ops image_hash_ops;
|
||||
|
||||
|
||||
@@ -91,6 +91,8 @@ static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
SD_VARLINK_DEFINE_INPUT(readOnly, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Whether to keep a pristine copy of the download separate from the locally installed image. Defaults to false."),
|
||||
SD_VARLINK_DEFINE_INPUT(keepDownload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Root directory for images. If not specified derived from the image class, and located below /var/lib/."),
|
||||
SD_VARLINK_DEFINE_INPUT(imageRoot, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_POLKIT_INPUT,
|
||||
SD_VARLINK_FIELD_COMMENT("A progress update, as percent value"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(progress, SD_VARLINK_FLOAT, SD_VARLINK_NULLABLE),
|
||||
|
||||
@@ -125,12 +125,13 @@ static void print_property(UdevEvent *event, const char *name, const char *value
|
||||
}
|
||||
}
|
||||
|
||||
static int find_gpt_root(UdevEvent *event, blkid_probe pr) {
|
||||
static int find_gpt_root(UdevEvent *event, blkid_probe pr, const char *loop_backing_fname) {
|
||||
|
||||
#if defined(SD_GPT_ROOT_NATIVE) && ENABLE_EFI
|
||||
|
||||
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
|
||||
sd_id128_t esp_or_xbootldr = SD_ID128_NULL;
|
||||
_cleanup_free_ char *root_label = NULL;
|
||||
bool found_esp_or_xbootldr = false;
|
||||
bool found_esp_or_xbootldr = false, need_esp_or_xbootldr;
|
||||
sd_id128_t root_id = SD_ID128_NULL;
|
||||
int r;
|
||||
|
||||
@@ -138,13 +139,42 @@ static int find_gpt_root(UdevEvent *event, blkid_probe pr) {
|
||||
assert(pr);
|
||||
|
||||
/* Iterate through the partitions on this disk, and see if the UEFI ESP or XBOOTLDR partition we
|
||||
* booted from is on it. If so, find the first root disk, and add a property indicating its partition
|
||||
* UUID. */
|
||||
* booted from is on it. If so, find the newest root partition, and add a property indicating its
|
||||
* partition UUID. We also do this if we are dealing with a loopback block device whose "backing
|
||||
* filename" field is set to the string "root". In the latter case we do not search for ESP or
|
||||
* XBOOTLDR. */
|
||||
|
||||
if (!device_is_devtype(dev, "disk")) {
|
||||
log_device_debug(dev, "Skipping GPT root logic on partition block device.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_loader_get_device_part_uuid(&esp_or_xbootldr);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT && !ERRNO_IS_NOT_SUPPORTED(r))
|
||||
return log_debug_errno(r, "Unable to determine loader partition UUID: %m");
|
||||
|
||||
log_device_debug(dev, "No loader partition UUID EFI variable set, not using partition data for search for default root block device.");
|
||||
|
||||
/* NB: if an ESP/xbootldr field is set, we always use that. We do this in order to guarantee
|
||||
* systematic behaviour. */
|
||||
if (!STRPTR_IN_SET(loop_backing_fname, "rootdisk", "rootdisk.raw")) {
|
||||
log_device_debug(dev, "Device is not a loopback block device with reference string 'root', not considering block device as default root block device.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* OK, we have now sufficiently identified this device as the right root "whole" device,
|
||||
* hence no need to bother with searching for ESP/XBOOTLDR */
|
||||
need_esp_or_xbootldr = false;
|
||||
} else
|
||||
/* We now know the the ESP/xbootldr UUID, but we cannot be sure yet it's on this block
|
||||
* device, hence look for it among partitions now */
|
||||
need_esp_or_xbootldr = true;
|
||||
|
||||
errno = 0;
|
||||
blkid_partlist pl = blkid_probe_get_partitions(pr);
|
||||
if (!pl)
|
||||
return errno_or_else(ENOMEM);
|
||||
return log_device_debug_errno(dev, errno_or_else(ENOMEM), "Failed to probe partitions: %m");
|
||||
|
||||
int nvals = blkid_partlist_numof_partitions(pl);
|
||||
for (int i = 0; i < nvals; i++) {
|
||||
@@ -158,27 +188,21 @@ static int find_gpt_root(UdevEvent *event, blkid_probe pr) {
|
||||
|
||||
r = blkid_partition_get_uuid_id128(pp, &id);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to get partition UUID, ignoring: %m");
|
||||
log_device_debug_errno(dev, r, "Failed to get partition UUID, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = blkid_partition_get_type_id128(pp, &type);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to get partition type UUID, ignoring: %m");
|
||||
log_device_debug_errno(dev, r, "Failed to get partition type UUID, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
label = blkid_partition_get_name(pp); /* returns NULL if empty */
|
||||
|
||||
if (sd_id128_in_set(type, SD_GPT_ESP, SD_GPT_XBOOTLDR)) {
|
||||
sd_id128_t esp_or_xbootldr;
|
||||
if (need_esp_or_xbootldr && sd_id128_in_set(type, SD_GPT_ESP, SD_GPT_XBOOTLDR)) {
|
||||
|
||||
/* We found an ESP or XBOOTLDR, let's see if it matches the ESP/XBOOTLDR we booted from. */
|
||||
|
||||
r = efi_loader_get_device_part_uuid(&esp_or_xbootldr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sd_id128_equal(id, esp_or_xbootldr))
|
||||
found_esp_or_xbootldr = true;
|
||||
|
||||
@@ -195,17 +219,20 @@ static int find_gpt_root(UdevEvent *event, blkid_probe pr) {
|
||||
if (sd_id128_is_null(root_id) || strverscmp_improved(label, root_label) > 0) {
|
||||
root_id = id;
|
||||
|
||||
r = free_and_strdup(&root_label, label);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (free_and_strdup(&root_label, label) < 0)
|
||||
return log_oom_debug();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We found the ESP/XBOOTLDR on this disk, and also found a root partition, nice! Let's export its
|
||||
* UUID */
|
||||
if (found_esp_or_xbootldr && !sd_id128_is_null(root_id))
|
||||
udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id));
|
||||
if (!need_esp_or_xbootldr || found_esp_or_xbootldr) {
|
||||
/* We found the ESP/XBOOTLDR on this disk (or we didn't need it) */
|
||||
udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_DISK", "1");
|
||||
|
||||
/* We found a root partition, nice! Let's export its UUID. */
|
||||
if (!sd_id128_is_null(root_id))
|
||||
udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id));
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@@ -437,9 +464,6 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) {
|
||||
udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT", "1");
|
||||
}
|
||||
|
||||
if (is_gpt)
|
||||
find_gpt_root(event, pr);
|
||||
|
||||
r = read_loopback_backing_inode(
|
||||
dev,
|
||||
fd,
|
||||
@@ -466,6 +490,9 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (is_gpt)
|
||||
find_gpt_root(event, pr, backing_fname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+94
-37
@@ -117,6 +117,7 @@ static bool arg_pass_ssh_key = true;
|
||||
static char *arg_ssh_key_type = NULL;
|
||||
static bool arg_discard_disk = true;
|
||||
struct ether_addr arg_network_provided_mac = {};
|
||||
static char **arg_smbios11 = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
@@ -133,6 +134,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline_extra, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_extra_drives, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_ssh_key_type, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_smbios11, strv_freep);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
@@ -167,6 +169,7 @@ static int help(void) {
|
||||
" --secure-boot=BOOL Enable searching for firmware supporting SecureBoot\n"
|
||||
" --firmware=PATH|list Select firmware definition file (or list available)\n"
|
||||
" --discard-disk=BOOL Control processing of discard requests\n"
|
||||
" -s --smbios11=STRING Pass an arbitrary SMBIOS Type #11 string to the VM\n"
|
||||
"\n%3$sSystem Identity:%4$s\n"
|
||||
" -M --machine=NAME Set the machine name for the VM\n"
|
||||
" --uuid=UUID Set a specific machine UUID for the VM\n"
|
||||
@@ -295,6 +298,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "firmware", required_argument, NULL, ARG_FIRMWARE },
|
||||
{ "discard-disk", required_argument, NULL, ARG_DISCARD_DISK },
|
||||
{ "background", required_argument, NULL, ARG_BACKGROUND },
|
||||
{ "smbios11", required_argument, NULL, 's' },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -304,7 +308,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argv);
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt_long(argc, argv, "+hD:i:M:nq", options, NULL)) >= 0)
|
||||
while ((c = getopt_long(argc, argv, "+hD:i:M:nqs:", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
case 'h':
|
||||
return help();
|
||||
@@ -564,6 +568,20 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return r;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (isempty(optarg)) {
|
||||
arg_smbios11 = strv_free(arg_smbios11);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!utf8_is_valid(optarg))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "SMBIOS Type 11 string is not UTF-8 clean, refusing: %s", optarg);
|
||||
|
||||
if (strv_extend(&arg_smbios11, optarg) < 0)
|
||||
return log_oom();
|
||||
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -908,9 +926,9 @@ static int cmdline_add_vsock(char ***cmdline, int vsock_fd) {
|
||||
|
||||
union sockaddr_union addr;
|
||||
socklen_t addr_len = sizeof addr.vm;
|
||||
r = getsockname(vsock_fd, &addr.sa, &addr_len);
|
||||
if (r < 0)
|
||||
if (getsockname(vsock_fd, &addr.sa, &addr_len) < 0)
|
||||
return -errno;
|
||||
|
||||
assert(addr_len >= sizeof addr.vm);
|
||||
assert(addr.vm.svm_family == AF_VSOCK);
|
||||
|
||||
@@ -921,6 +939,73 @@ static int cmdline_add_vsock(char ***cmdline, int vsock_fd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmdline_add_kernel_cmdline(char ***cmdline, const char *kernel) {
|
||||
assert(cmdline);
|
||||
|
||||
if (strv_isempty(arg_kernel_cmdline_extra))
|
||||
return 0;
|
||||
|
||||
_cleanup_free_ char *kcl = strv_join(arg_kernel_cmdline_extra, " ");
|
||||
if (!kcl)
|
||||
return log_oom();
|
||||
|
||||
if (kernel) {
|
||||
if (strv_extend_many(cmdline, "-append", kcl) < 0)
|
||||
return log_oom();
|
||||
} else {
|
||||
if (!ARCHITECTURE_SUPPORTS_SMBIOS) {
|
||||
log_warning("Cannot append extra args to kernel cmdline, native architecture doesn't support SMBIOS, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *escaped_kcl = NULL;
|
||||
escaped_kcl = escape_qemu_value(kcl);
|
||||
if (!escaped_kcl)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend(cmdline, "-smbios") < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extendf(cmdline, "type=11,value=io.systemd.stub.kernel-cmdline-extra=%s", escaped_kcl) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend(cmdline, "-smbios") < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extendf(cmdline, "type=11,value=io.systemd.boot.kernel-cmdline-extra=%s", escaped_kcl) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmdline_add_smbios11(char ***cmdline) {
|
||||
assert(cmdline);
|
||||
|
||||
if (strv_isempty(arg_smbios11))
|
||||
return 0;
|
||||
|
||||
if (!ARCHITECTURE_SUPPORTS_SMBIOS) {
|
||||
log_warning("Cannot issue SMBIOS Type #11 strings, native architecture doesn't support SMBIOS, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STRV_FOREACH(i, arg_smbios11) {
|
||||
_cleanup_free_ char *escaped = NULL;
|
||||
escaped = escape_qemu_value(*i);
|
||||
if (!escaped)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend(cmdline, "-smbios") < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extendf(cmdline, "type=11,value=%s", escaped) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_tpm(
|
||||
sd_bus *bus,
|
||||
const char *scope,
|
||||
@@ -1864,41 +1949,13 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (ARCHITECTURE_SUPPORTS_SMBIOS) {
|
||||
_cleanup_free_ char *kcl = strv_join(arg_kernel_cmdline_extra, " "), *escaped_kcl = NULL;
|
||||
if (!kcl)
|
||||
return log_oom();
|
||||
r = cmdline_add_kernel_cmdline(&cmdline, kernel);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (kernel) {
|
||||
r = strv_extend_many(&cmdline, "-append", kcl);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
} else {
|
||||
if (ARCHITECTURE_SUPPORTS_SMBIOS) {
|
||||
escaped_kcl = escape_qemu_value(kcl);
|
||||
if (!escaped_kcl)
|
||||
log_oom();
|
||||
|
||||
r = strv_extend(&cmdline, "-smbios");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "type=11,value=io.systemd.stub.kernel-cmdline-extra=%s", escaped_kcl);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extend(&cmdline, "-smbios");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "type=11,value=io.systemd.boot.kernel-cmdline-extra=%s", escaped_kcl);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
} else
|
||||
log_warning("Cannot append extra args to kernel cmdline, native architecture doesn't support SMBIOS, ignoring");
|
||||
}
|
||||
} else
|
||||
log_warning("Cannot append extra args to kernel cmdline, native architecture doesn't support SMBIOS");
|
||||
r = cmdline_add_smbios11(&cmdline);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* disable TPM autodetection if the user's hardware doesn't support it */
|
||||
if (!ARCHITECTURE_SUPPORTS_TPM) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
|
||||
SourcePath=/proc/cmdline
|
||||
Before=initrd-root-fs.target
|
||||
After=imports.target
|
||||
Requires=systemd-fsck-root.service
|
||||
After=systemd-fsck-root.service
|
||||
After=blockdev@dev-sdx1.target
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
|
||||
SourcePath=/proc/cmdline
|
||||
Before=initrd-root-fs.target
|
||||
After=imports.target
|
||||
Requires=systemd-fsck-root.service
|
||||
After=systemd-fsck-root.service
|
||||
After=blockdev@dev-disk-by\x2dlabel-Root.target
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
|
||||
SourcePath=/proc/cmdline
|
||||
Before=initrd-root-fs.target
|
||||
After=imports.target
|
||||
Requires=systemd-fsck-root.service
|
||||
After=systemd-fsck-root.service
|
||||
After=blockdev@dev-disk-by\x2duuid-3f5ad593\x2d4546\x2d4a94\x2da374\x2dbcfb68aa11f7.target
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
|
||||
SourcePath=/proc/cmdline
|
||||
Before=initrd-root-fs.target
|
||||
After=imports.target
|
||||
Requires=systemd-fsck-root.service
|
||||
After=systemd-fsck-root.service
|
||||
After=blockdev@dev-disk-by\x2dpartuuid-3f5ad593\x2d4546\x2d4a94\x2da374\x2dbcfb68aa11f7.target
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
|
||||
SourcePath=/proc/cmdline
|
||||
Before=initrd-root-fs.target
|
||||
After=imports.target
|
||||
|
||||
[Mount]
|
||||
What=rootfs
|
||||
|
||||
@@ -81,7 +81,7 @@ cat /proc/cmdline
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
systemctl start import0.service
|
||||
systemctl start systemd-import@var-lib-confexts-importtest9.service
|
||||
cmp /var/tmp/importtest /var/lib/confexts/importtest9/importtest
|
||||
|
||||
# Verify generic service calls, too
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Image Downloads (Pre)
|
||||
Documentation=man:systemd.special(7)
|
||||
Before=imports.target
|
||||
RefuseManualStart=yes
|
||||
@@ -0,0 +1,12 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Image Downloads
|
||||
Documentation=man:systemd.special(7)
|
||||
@@ -382,6 +382,15 @@ units = [
|
||||
'conditions' : ['ENABLE_IMPORTD'],
|
||||
'symlinks' : ['sockets.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'imports-pre.target',
|
||||
'conditions' : ['ENABLE_IMPORTD'],
|
||||
},
|
||||
{
|
||||
'file' : 'imports.target',
|
||||
'conditions' : ['ENABLE_IMPORTD'],
|
||||
'symlinks' : ['sysinit.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-initctl.service.in',
|
||||
'conditions' : ['HAVE_SYSV_COMPAT'],
|
||||
@@ -447,6 +456,7 @@ units = [
|
||||
'conditions' : ['ENABLE_LOGIND'],
|
||||
'symlinks' : ['multi-user.target.wants/', 'dbus-org.freedesktop.login1.service'],
|
||||
},
|
||||
{ 'file' : 'systemd-loop@.service' },
|
||||
{
|
||||
'file' : 'systemd-machine-id-commit.service',
|
||||
'symlinks' : ['sysinit.target.wants/'],
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Attach File %f to Loopback Block Device
|
||||
Documentation=man:systemd-loop@.service(8)
|
||||
DefaultDependencies=no
|
||||
RequiresMountsFor=%f
|
||||
IgnoreOnIsolate=yes
|
||||
Wants=modprobe@loop.service
|
||||
After=modprobe@loop.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
TimeoutSec=infinity
|
||||
ExecStart=systemd-dissect --attach --loop-ref-auto --quiet %f
|
||||
ExecStop=systemd-dissect --detach %f
|
||||
Reference in New Issue
Block a user