mirror of
https://github.com/systemd/systemd.git
synced 2025-03-31 14:50:15 +03:00
sysupdate.d: Add way to drop binaries into $BOOT
As described in the BLS, we should place binaries into the XBOOTLDR directory if it is available, otherwise into the ESP. Thus, we might need to put binaries into /boot or into /efi depending on the existence of the XBOOTLDR partition. With this change, we introduce a new PathRelativeTo= config option that makes this functionality possible
This commit is contained in:
parent
58a5a2362c
commit
0470f91983
@ -81,8 +81,8 @@
|
||||
|
||||
<listitem><para>Finally, a file <literal>https://download.example.com/foobarOS_47.efi.xz</literal> (a
|
||||
unified kernel, as per <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader
|
||||
Specification</ulink> Type #2) should be downloaded, decompressed and written to the ESP file system,
|
||||
i.e. to <filename>EFI/Linux/foobarOS_47.efi</filename> in the ESP.</para></listitem>
|
||||
Specification</ulink> Type #2) should be downloaded, decompressed and written to the $BOOT file system,
|
||||
i.e. to <filename>EFI/Linux/foobarOS_47.efi</filename> in the ESP or XBOOTLDR partition.</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>The version-independent generalization of this would be (using the special marker
|
||||
@ -98,7 +98,7 @@
|
||||
set to <literal>foobarOS_@v_verity</literal>.</para></listitem>
|
||||
|
||||
<listitem><para>A transfer of a file <literal>https://download.example.com/foobarOS_@v.efi.xz</literal>
|
||||
→ a local file <filename>/efi/EFI/Linux/foobarOS_@v.efi</filename>.</para></listitem>
|
||||
→ a local file <filename>$BOOT/EFI/Linux/foobarOS_@v.efi</filename>.</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>An update can only complete if the relevant URLs provide their resources for the same version,
|
||||
@ -569,6 +569,23 @@
|
||||
<citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>PathRelativeTo=</varname></term>
|
||||
|
||||
<listitem><para>Specifies what partition <varname>Path=</varname> should be relative to. Takes one of
|
||||
<constant>root</constant>, <constant>esp</constant>, <constant>xbootldr</constant>, or <constant>boot</constant>.
|
||||
If unspecified, defaults to <constant>root</constant>.</para>
|
||||
|
||||
<para>If set to <constant>boot</constant>, the specified <varname>Path=</varname> will be resolved
|
||||
relative to the mount point of the $BOOT partition (i.e. the ESP or XBOOTLDR), as defined by the
|
||||
<ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader
|
||||
Specification</ulink>.</para>
|
||||
|
||||
<para>The values <constant>esp</constant>, <constant>xbootldr</constant>, and
|
||||
<constant>boot</constant> are only supported when <varname>Type=</varname> is set to
|
||||
<constant>regular-file</constant> or <constant>directory</constant>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>MatchPattern=</varname></term>
|
||||
|
||||
@ -820,7 +837,8 @@ MatchPattern=foobarOS_@v.efi.xz
|
||||
|
||||
[Target]
|
||||
Type=regular-file
|
||||
Path=/efi/EFI/Linux
|
||||
Path=/EFI/Linux
|
||||
PathRelativeTo=boot
|
||||
MatchPattern=foobarOS_@v+@l-@d.efi \
|
||||
foobarOS_@v+@l.efi \
|
||||
foobarOS_@v.efi
|
||||
@ -829,12 +847,12 @@ TriesLeft=3
|
||||
TriesDone=0
|
||||
InstancesMax=2</programlisting></para>
|
||||
|
||||
<para>The above installs a unified kernel image into the ESP (which is mounted to
|
||||
<filename>/efi/</filename>), as per <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot
|
||||
Loader Specification</ulink> Type #2. This defines three possible patterns for the names of the
|
||||
kernel images, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot
|
||||
Assessment</ulink>, and ensures when installing new kernels, they are set up with 3 tries left. No
|
||||
more than two parallel kernels are kept.</para>
|
||||
<para>The above installs a unified kernel image into the $BOOT partition, as per
|
||||
<ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader
|
||||
Specification</ulink> Type #2. This defines three possible patterns for the names of the kernel
|
||||
images, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot Assessment</ulink>,
|
||||
and ensures when installing new kernels, they are set up with 3 tries left. No more than two parallel
|
||||
kernels are kept.</para>
|
||||
|
||||
<para>With this setup the web server would serve the following files, for a hypothetical version 7 of
|
||||
the OS:</para>
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "find-esp.h"
|
||||
#include "glyph-util.h"
|
||||
#include "gpt.h"
|
||||
#include "hexdecoct.h"
|
||||
@ -520,6 +521,12 @@ int resource_resolve_path(
|
||||
|
||||
assert(rr);
|
||||
|
||||
if (IN_SET(rr->path_relative_to, PATH_RELATIVE_TO_ESP, PATH_RELATIVE_TO_XBOOTLDR, PATH_RELATIVE_TO_BOOT) &&
|
||||
!IN_SET(rr->type, RESOURCE_REGULAR_FILE, RESOURCE_DIRECTORY))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Paths relative to %s are only allowed for regular-file or directory resources.",
|
||||
path_relative_to_to_string(rr->path_relative_to));
|
||||
|
||||
if (rr->path_auto) {
|
||||
struct stat orig_root_stats;
|
||||
|
||||
@ -595,12 +602,31 @@ int resource_resolve_path(
|
||||
|
||||
r = get_block_device_harder_fd(fd, &d);
|
||||
|
||||
} else if (RESOURCE_IS_FILESYSTEM(rr->type) && root) {
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
} else if (RESOURCE_IS_FILESYSTEM(rr->type)) {
|
||||
_cleanup_free_ char *resolved = NULL, *relative_to = NULL;
|
||||
ChaseFlags chase_flags = CHASE_PREFIX_ROOT;
|
||||
|
||||
r = chase(rr->path, root, CHASE_PREFIX_ROOT, &resolved, NULL);
|
||||
if (rr->path_relative_to == PATH_RELATIVE_TO_ROOT) {
|
||||
relative_to = strdup(empty_to_root(root));
|
||||
if (!relative_to)
|
||||
return log_oom();
|
||||
} else { /* boot, esp, or xbootldr */
|
||||
r = 0;
|
||||
if (IN_SET(rr->path_relative_to, PATH_RELATIVE_TO_BOOT, PATH_RELATIVE_TO_XBOOTLDR))
|
||||
r = find_xbootldr_and_warn(root, NULL, /* unprivileged_mode= */ -1, &relative_to, NULL, NULL);
|
||||
if (r == -ENOKEY || rr->path_relative_to == PATH_RELATIVE_TO_ESP)
|
||||
r = find_esp_and_warn(root, NULL, -1, &relative_to, NULL, NULL, NULL, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve $BOOT: %m");
|
||||
log_debug("Resolved $BOOT to '%s'", relative_to);
|
||||
|
||||
/* Since this partition is read from EFI, there should be no symlinks */
|
||||
chase_flags |= CHASE_PROHIBIT_SYMLINKS;
|
||||
}
|
||||
|
||||
r = chase(rr->path, relative_to, chase_flags, &resolved, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve '%s': %m", rr->path);
|
||||
return log_error_errno(r, "Failed to resolve '%s' (relative to '%s'): %m", rr->path, relative_to);
|
||||
|
||||
free_and_replace(rr->path, resolved);
|
||||
return 0;
|
||||
@ -641,3 +667,12 @@ static const char *resource_type_table[_RESOURCE_TYPE_MAX] = {
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(resource_type, ResourceType);
|
||||
|
||||
static const char *path_relative_to_table[_PATH_RELATIVE_TO_MAX] = {
|
||||
[PATH_RELATIVE_TO_ROOT] = "root",
|
||||
[PATH_RELATIVE_TO_ESP] = "esp",
|
||||
[PATH_RELATIVE_TO_XBOOTLDR] = "xbootldr",
|
||||
[PATH_RELATIVE_TO_BOOT] = "boot",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(path_relative_to, PathRelativeTo);
|
||||
|
@ -66,12 +66,24 @@ static inline bool RESOURCE_IS_URL(ResourceType t) {
|
||||
RESOURCE_URL_FILE);
|
||||
}
|
||||
|
||||
typedef enum PathRelativeTo {
|
||||
/* Please make sure to folow the naming of the corresponding PartitionDesignator enum values,
|
||||
* where this makes sense, like for the following three. */
|
||||
PATH_RELATIVE_TO_ROOT,
|
||||
PATH_RELATIVE_TO_ESP,
|
||||
PATH_RELATIVE_TO_XBOOTLDR,
|
||||
PATH_RELATIVE_TO_BOOT, /* Refers to $BOOT from the BLS. No direct counterpart in PartitionDesignator */
|
||||
_PATH_RELATIVE_TO_MAX,
|
||||
_PATH_RELATIVE_TO_INVALID = -EINVAL,
|
||||
} PathRelativeTo;
|
||||
|
||||
struct Resource {
|
||||
ResourceType type;
|
||||
|
||||
/* Where to look for instances, and what to match precisely */
|
||||
char *path;
|
||||
bool path_auto; /* automatically find root path (only available if target resource, not source resource) */
|
||||
PathRelativeTo path_relative_to;
|
||||
char **patterns;
|
||||
GptPartitionType partition_type;
|
||||
bool partition_type_set;
|
||||
@ -94,3 +106,6 @@ int resource_resolve_path(Resource *rr, const char *root, const char *node);
|
||||
|
||||
ResourceType resource_type_from_string(const char *s) _pure_;
|
||||
const char *resource_type_to_string(ResourceType t) _const_;
|
||||
|
||||
PathRelativeTo path_relative_to_from_string(const char *s) _pure_;
|
||||
const char *path_relative_to_to_string(PathRelativeTo r) _const_;
|
||||
|
@ -297,7 +297,6 @@ static int config_parse_resource_path(
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
Resource *rr = ASSERT_PTR(data);
|
||||
int r;
|
||||
@ -327,6 +326,9 @@ static int config_parse_resource_path(
|
||||
|
||||
static DEFINE_CONFIG_PARSE_ENUM(config_parse_resource_type, resource_type, ResourceType, "Invalid resource type");
|
||||
|
||||
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_resource_path_relto, path_relative_to, PathRelativeTo,
|
||||
PATH_RELATIVE_TO_ROOT, "Invalid PathRelativeTo= value");
|
||||
|
||||
static int config_parse_resource_ptype(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
@ -418,27 +420,29 @@ int transfer_read_definition(Transfer *t, const char *path) {
|
||||
assert(path);
|
||||
|
||||
ConfigTableItem table[] = {
|
||||
{ "Transfer", "MinVersion", config_parse_min_version, 0, &t->min_version },
|
||||
{ "Transfer", "ProtectVersion", config_parse_protect_version, 0, &t->protected_versions },
|
||||
{ "Transfer", "Verify", config_parse_bool, 0, &t->verify },
|
||||
{ "Source", "Type", config_parse_resource_type, 0, &t->source.type },
|
||||
{ "Source", "Path", config_parse_resource_path, 0, &t->source },
|
||||
{ "Source", "MatchPattern", config_parse_resource_pattern, 0, &t->source.patterns },
|
||||
{ "Target", "Type", config_parse_resource_type, 0, &t->target.type },
|
||||
{ "Target", "Path", config_parse_resource_path, 0, &t->target },
|
||||
{ "Target", "MatchPattern", config_parse_resource_pattern, 0, &t->target.patterns },
|
||||
{ "Target", "MatchPartitionType", config_parse_resource_ptype, 0, &t->target },
|
||||
{ "Target", "PartitionUUID", config_parse_partition_uuid, 0, t },
|
||||
{ "Target", "PartitionFlags", config_parse_partition_flags, 0, t },
|
||||
{ "Target", "PartitionNoAuto", config_parse_tristate, 0, &t->no_auto },
|
||||
{ "Target", "PartitionGrowFileSystem", config_parse_tristate, 0, &t->growfs },
|
||||
{ "Target", "ReadOnly", config_parse_tristate, 0, &t->read_only },
|
||||
{ "Target", "Mode", config_parse_mode, 0, &t->mode },
|
||||
{ "Target", "TriesLeft", config_parse_uint64, 0, &t->tries_left },
|
||||
{ "Target", "TriesDone", config_parse_uint64, 0, &t->tries_done },
|
||||
{ "Target", "InstancesMax", config_parse_instances_max, 0, &t->instances_max },
|
||||
{ "Target", "RemoveTemporary", config_parse_bool, 0, &t->remove_temporary },
|
||||
{ "Target", "CurrentSymlink", config_parse_current_symlink, 0, &t->current_symlink },
|
||||
{ "Transfer", "MinVersion", config_parse_min_version, 0, &t->min_version },
|
||||
{ "Transfer", "ProtectVersion", config_parse_protect_version, 0, &t->protected_versions },
|
||||
{ "Transfer", "Verify", config_parse_bool, 0, &t->verify },
|
||||
{ "Source", "Type", config_parse_resource_type, 0, &t->source.type },
|
||||
{ "Source", "Path", config_parse_resource_path, 0, &t->source },
|
||||
{ "Source", "PathRelativeTo", config_parse_resource_path_relto, 0, &t->source.path_relative_to },
|
||||
{ "Source", "MatchPattern", config_parse_resource_pattern, 0, &t->source.patterns },
|
||||
{ "Target", "Type", config_parse_resource_type, 0, &t->target.type },
|
||||
{ "Target", "Path", config_parse_resource_path, 0, &t->target },
|
||||
{ "Target", "PathRelativeTo", config_parse_resource_path_relto, 0, &t->target.path_relative_to },
|
||||
{ "Target", "MatchPattern", config_parse_resource_pattern, 0, &t->target.patterns },
|
||||
{ "Target", "MatchPartitionType", config_parse_resource_ptype, 0, &t->target },
|
||||
{ "Target", "PartitionUUID", config_parse_partition_uuid, 0, t },
|
||||
{ "Target", "PartitionFlags", config_parse_partition_flags, 0, t },
|
||||
{ "Target", "PartitionNoAuto", config_parse_tristate, 0, &t->no_auto },
|
||||
{ "Target", "PartitionGrowFileSystem", config_parse_tristate, 0, &t->growfs },
|
||||
{ "Target", "ReadOnly", config_parse_tristate, 0, &t->read_only },
|
||||
{ "Target", "Mode", config_parse_mode, 0, &t->mode },
|
||||
{ "Target", "TriesLeft", config_parse_uint64, 0, &t->tries_left },
|
||||
{ "Target", "TriesDone", config_parse_uint64, 0, &t->tries_done },
|
||||
{ "Target", "InstancesMax", config_parse_instances_max, 0, &t->instances_max },
|
||||
{ "Target", "RemoveTemporary", config_parse_bool, 0, &t->remove_temporary },
|
||||
{ "Target", "CurrentSymlink", config_parse_current_symlink, 0, &t->current_symlink },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -30,6 +30,7 @@ size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
|
||||
EOF
|
||||
|
||||
rm -rf /var/tmp/72-dirs
|
||||
mkdir -p /var/tmp/72-dirs
|
||||
|
||||
rm -rf /var/tmp/72-defs
|
||||
mkdir -p /var/tmp/72-defs
|
||||
@ -74,6 +75,30 @@ MatchPattern=dir-@v
|
||||
InstancesMax=3
|
||||
EOF
|
||||
|
||||
cat >/var/tmp/72-defs/04-fourth.conf <<"EOF"
|
||||
[Source]
|
||||
Type=regular-file
|
||||
Path=/var/tmp/72-source
|
||||
MatchPattern=uki-@v.efi
|
||||
|
||||
[Target]
|
||||
Type=regular-file
|
||||
Path=/EFI/Linux
|
||||
PathRelativeTo=boot
|
||||
MatchPattern=uki_@v+@l-@d.efi \
|
||||
uki_@v+@l.efi \
|
||||
uki_@v.efi
|
||||
Mode=0444
|
||||
TriesLeft=3
|
||||
TriesDone=0
|
||||
InstancesMax=2
|
||||
EOF
|
||||
|
||||
rm -rf /var/tmp/72-esp /var/tmp/72-xbootldr
|
||||
mkdir -p /var/tmp/72-esp/EFI/Linux /var/tmp/72-xbootldr/EFI/Linux
|
||||
export SYSTEMD_ESP_PATH=/var/tmp/72-esp
|
||||
export SYSTEMD_XBOOTLDR_PATH=/var/tmp/72-xbootldr
|
||||
|
||||
rm -rf /var/tmp/72-source
|
||||
mkdir -p /var/tmp/72-source
|
||||
|
||||
@ -83,13 +108,16 @@ new_version() {
|
||||
dd if=/dev/urandom of="/var/tmp/72-source/part2-$1.raw" bs=1024 count=1024
|
||||
gzip -k -f "/var/tmp/72-source/part2-$1.raw"
|
||||
|
||||
# Create a random "UKI" payload
|
||||
echo $RANDOM >"/var/tmp/72-source/uki-$1.efi"
|
||||
|
||||
# Create tarball of a directory
|
||||
mkdir -p "/var/tmp/72-source/dir-$1"
|
||||
echo $RANDOM >"/var/tmp/72-source/dir-$1/foo.txt"
|
||||
echo $RANDOM >"/var/tmp/72-source/dir-$1/bar.txt"
|
||||
|
||||
tar --numeric-owner -C "/var/tmp/72-source/dir-$1/" -czf "/var/tmp/72-source/dir-$1.tar.gz" .
|
||||
|
||||
( cd /var/tmp/72-source/ && sha256sum part* dir-*.tar.gz >SHA256SUMS )
|
||||
( cd /var/tmp/72-source/ && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS )
|
||||
}
|
||||
|
||||
update_now() {
|
||||
@ -103,8 +131,16 @@ update_now() {
|
||||
|
||||
verify_version() {
|
||||
# Expects: version ID + sector offset of both partitions to compare
|
||||
|
||||
# Check the partitions
|
||||
dd if=/var/tmp/72-joined.raw bs=1024 skip="$2" count=1024 | cmp "/var/tmp/72-source/part1-$1.raw"
|
||||
dd if=/var/tmp/72-joined.raw bs=1024 skip="$3" count=1024 | cmp "/var/tmp/72-source/part2-$1.raw"
|
||||
|
||||
# Check the UKI
|
||||
cmp "/var/tmp/72-source/uki-$1.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$1+3-0.efi"
|
||||
test -z "$(ls -A /var/tmp/72-esp/EFI/Linux)"
|
||||
|
||||
# Check the directories
|
||||
cmp "/var/tmp/72-source/dir-$1/foo.txt" /var/tmp/72-dirs/current/foo.txt
|
||||
cmp "/var/tmp/72-source/dir-$1/bar.txt" /var/tmp/72-dirs/current/bar.txt
|
||||
}
|
||||
@ -123,6 +159,7 @@ verify_version v2 2048 4096
|
||||
new_version v3
|
||||
update_now
|
||||
verify_version v3 1024 3072
|
||||
test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1+3-0.efi"
|
||||
|
||||
# Create fourth version, and update through a file:// URL. This should be
|
||||
# almost as good as testing HTTP, but is simpler for us to set up. file:// is
|
||||
@ -163,7 +200,7 @@ update_now
|
||||
verify_version v4 2048 4096
|
||||
|
||||
rm /var/tmp/72-joined.raw
|
||||
rm -r /var/tmp/72-dirs /var/tmp/72-defs /var/tmp/72-source
|
||||
rm -r /var/tmp/72-{dirs,defs,source,xbootldr,esp}
|
||||
|
||||
echo OK >/testok
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user