Skip to content

Commit

Permalink
generator: Fixes for Android Boot environment
Browse files Browse the repository at this point in the history
In Android Boot environment we do not parse ostree= karg to determine
what directory to boot into, alternatively we do this based on the
androidboot.slot_suffix= karg. But we do set ostree=true karg to denote
that we are indeed booting an ostree environment (required for some
systemd unit files). This change accounts for this approach in the
systemd generator. In this case androidboot.slot_suffix= points you to
/ostree/root.[a|b] and then that points you to the directory to boot
into in /ostree/deploy... Here is what a cmdline may look like in this
type of environment:

androidboot.slot_suffix=_a androidboot.bootdevice=*.ufshc root=PARTLABEL=system_a root=UUID=76a22bf4-f153-4541-b6c7-0332c0dfaeac rw ostree=true loglevel=4 acpi=off console=ttyAMA0 systemd.show_status=auto libahci.ignore_sss=1 slub_debug=FPZ fsck.mode=skip rcupdate.rcu_normal_after_boot=0 rcupdate.rcu_expedited=1

Signed-off-by: Eric Curtin <[email protected]>
  • Loading branch information
ericcurtin committed Feb 21, 2024
1 parent 8f98143 commit 4a71845
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 30 deletions.
17 changes: 10 additions & 7 deletions src/libostree/ostree-impl-system-generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ require_internal_units (const char *normal_dir, const char *early_dir, const cha

/* Generate var.mount */
static gboolean
fstab_generator (const char *ostree_cmdline, const char *normal_dir, const char *early_dir,
const char *late_dir, GError **error)
fstab_generator (const char *ostree_target, const bool is_aboot, const char *normal_dir,
const char *early_dir, const char *late_dir, GError **error)
{
#ifdef HAVE_LIBMOUNT
/* Not currently cancellable, but define a var in case we care later */
Expand All @@ -144,7 +144,8 @@ fstab_generator (const char *ostree_cmdline, const char *normal_dir, const char
* mounted yet.
*/
g_autofree char *stateroot = NULL;
if (!_ostree_sysroot_parse_bootlink (ostree_cmdline, NULL, &stateroot, NULL, NULL, error))
if (!_ostree_sysroot_parse_bootlink (ostree_target, is_aboot, NULL, &stateroot, NULL, NULL,
error))
return glnx_prefix_error (error, "Parsing stateroot");

/* Load /etc/fstab if it exists, and look for a /var mount */
Expand Down Expand Up @@ -261,17 +262,19 @@ _ostree_impl_system_generator (const char *normal_dir, const char *early_dir, co
if (!cmdline)
return glnx_throw (error, "Failed to read /proc/cmdline");

g_autofree char *ostree_cmdline = otcore_find_proc_cmdline_key (cmdline, "ostree");

g_autoptr (GError) otcore_get_ostree_target_error = NULL;
g_autofree char *ostree_target = NULL;
bool is_aboot = false;
/* This could happen in CoreOS live environments, where we hackily mock
* the `ostree=` karg for `ostree-prepare-root.service` specifically, but
* otherwise that karg doesn't exist on the real command-line. */
if (!ostree_cmdline)
if (!otcore_get_ostree_target (cmdline, &is_aboot, &ostree_target,
&otcore_get_ostree_target_error))
return TRUE;

if (!require_internal_units (normal_dir, early_dir, late_dir, error))
return FALSE;
if (!fstab_generator (ostree_cmdline, normal_dir, early_dir, late_dir, error))
if (!fstab_generator (ostree_target, is_aboot, normal_dir, early_dir, late_dir, error))
return FALSE;

return TRUE;
Expand Down
9 changes: 6 additions & 3 deletions src/libostree/ostree-sysroot-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#pragma once

#include <stdbool.h>

#include "libglnx.h"
#include "ostree-bootloader.h"
#include "ostree.h"
Expand Down Expand Up @@ -177,8 +179,9 @@ gboolean _ostree_sysroot_parse_bootdir_name (const char *name, char **out_osname
gboolean _ostree_sysroot_list_all_boot_directories (OstreeSysroot *self, char ***out_bootdirs,
GCancellable *cancellable, GError **error);

gboolean _ostree_sysroot_parse_bootlink (const char *bootlink, int *out_entry_bootversion,
char **out_osname, char **out_bootcsum,
int *out_treebootserial, GError **error);
gboolean _ostree_sysroot_parse_bootlink (const char *bootlink, const bool is_aboot,
int *out_entry_bootversion, char **out_osname,
char **out_bootcsum, int *out_treebootserial,
GError **error);

G_END_DECLS
37 changes: 30 additions & 7 deletions src/libostree/ostree-sysroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -724,24 +724,44 @@ load_origin (OstreeSysroot *self, OstreeDeployment *deployment, GCancellable *ca

// Parse the kernel argument ostree=
gboolean
_ostree_sysroot_parse_bootlink (const char *bootlink, int *out_entry_bootversion, char **out_osname,
char **out_bootcsum, int *out_treebootserial, GError **error)
_ostree_sysroot_parse_bootlink (const char *bootlink, const bool is_aboot,
int *out_entry_bootversion, char **out_osname, char **out_bootcsum,
int *out_treebootserial, GError **error)
{
static gsize regex_initialized;
static GRegex *regex;
const char *to_parse = bootlink;
g_autofree char *symlink_val = NULL;
if (is_aboot)
{
symlink_val = glnx_readlinkat_malloc (-1, bootlink, NULL, error);
if (!symlink_val)
return glnx_throw (error, "Failed to read '%s' symlink", bootlink);

to_parse = symlink_val;
}

if (g_once_init_enter (&regex_initialized))
{
regex = g_regex_new ("^/ostree/boot.([01])/([^/]+)/([^/]+)/([0-9]+)$", 0, 0, NULL);
regex = g_regex_new (is_aboot ? "^deploy/([^/]+)/"
: "^/ostree/boot.([01])/([^/]+)/([^/]+)/([0-9]+)$",
0, 0, NULL);
g_assert (regex);
g_once_init_leave (&regex_initialized, 1);
}

g_autoptr (GMatchInfo) match = NULL;
if (!g_regex_match (regex, bootlink, 0, &match))
if (!g_regex_match (regex, to_parse, 0, &match))
return glnx_throw (error,
"Invalid ostree= argument '%s', expected "
"ostree=/ostree/boot.BOOTVERSION/OSNAME/BOOTCSUM/TREESERIAL",
bootlink);
"ostree=/ostree/boot.BOOTVERSION/OSNAME/BOOTCSUM/TREESERIAL or aboot method",
to_parse);

if (is_aboot)
{
*out_osname = g_match_info_fetch (match, 1);
return TRUE;
}

g_autofree char *bootversion_str = g_match_info_fetch (match, 1);
g_autofree char *treebootserial_str = g_match_info_fetch (match, 4);
Expand Down Expand Up @@ -775,7 +795,10 @@ parse_deployment (OstreeSysroot *self, const char *boot_link, OstreeDeployment *
g_autofree char *osname = NULL;
g_autofree char *bootcsum = NULL;
int treebootserial = -1;
if (!_ostree_sysroot_parse_bootlink (boot_link, &entry_boot_version, &osname, &bootcsum,

// Note is_boot should always be false here, this boot_link is taken from BLS file, not
// /proc/cmdline, BLS files are present in aboot images
if (!_ostree_sysroot_parse_bootlink (boot_link, false, &entry_boot_version, &osname, &bootcsum,
&treebootserial, error))
return FALSE;

Expand Down
4 changes: 3 additions & 1 deletion src/libotcore/otcore-prepare-root.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ otcore_find_proc_cmdline_key (const char *cmdline, const char *key)
//
// If invalid data is found, @error will be set.
gboolean
otcore_get_ostree_target (const char *cmdline, char **out_target, GError **error)
otcore_get_ostree_target (const char *cmdline, bool *is_aboot, char **out_target, GError **error)
{
g_assert (cmdline);
g_assert (out_target && *out_target == NULL);
Expand All @@ -84,8 +84,10 @@ otcore_get_ostree_target (const char *cmdline, char **out_target, GError **error

// First, handle the Android boot case
g_autofree char *slot_suffix = otcore_find_proc_cmdline_key (cmdline, "androidboot.slot_suffix");
*is_aboot = false;
if (slot_suffix)
{
*is_aboot = true;
if (strcmp (slot_suffix, "_a") == 0)
{
*out_target = g_strdup (slot_a);
Expand Down
3 changes: 2 additions & 1 deletion src/libotcore/otcore.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ gboolean otcore_validate_ed25519_signature (GBytes *data, GBytes *pubkey, GBytes
bool *out_valid, GError **error);

char *otcore_find_proc_cmdline_key (const char *cmdline, const char *key);
gboolean otcore_get_ostree_target (const char *cmdline, char **out_target, GError **error);
gboolean otcore_get_ostree_target (const char *cmdline, bool *is_aboot, char **out_target,
GError **error);

GKeyFile *otcore_load_config (int rootfs, const char *filename, GError **error);

Expand Down
3 changes: 2 additions & 1 deletion src/switchroot/ostree-prepare-root.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ resolve_deploy_path (const char *root_mountpoint)

g_autoptr (GError) error = NULL;
g_autofree char *ostree_target = NULL;
if (!otcore_get_ostree_target (kernel_cmdline, &ostree_target, &error))
bool is_aboot = false;
if (!otcore_get_ostree_target (kernel_cmdline, &is_aboot, &ostree_target, &error))
errx (EXIT_FAILURE, "Failed to determine ostree target: %s", error->message);
if (!ostree_target)
errx (EXIT_FAILURE, "No ostree target found");
Expand Down
22 changes: 12 additions & 10 deletions tests/test-otcore.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,46 +36,48 @@ test_prepare_root_cmdline (void)
{
g_autoptr (GError) error = NULL;
g_autofree char *target = NULL;
bool is_aboot = false;

static const char *notfound_cases[]
= { "", "foo", "foo=bar baz sometest", "xostree foo", "xostree=blah bar", NULL };
for (const char **iter = notfound_cases; iter && *iter; iter++)
{
const char *tcase = *iter;
g_assert (otcore_get_ostree_target (tcase, &target, &error));
g_assert (otcore_get_ostree_target (tcase, &is_aboot, &target, &error));
g_assert_no_error (error);
g_assert (target == NULL);
}

// Test the default ostree=
g_assert (otcore_get_ostree_target ("blah baz=blah ostree=/foo/bar somearg", &target, &error));
g_assert (otcore_get_ostree_target ("blah baz=blah ostree=/foo/bar somearg", &is_aboot, &target,
&error));
g_assert_no_error (error);
g_assert_cmpstr (target, ==, "/foo/bar");
free (g_steal_pointer (&target));

// Test android boot
g_assert (otcore_get_ostree_target ("blah baz=blah androidboot.slot_suffix=_b somearg", &target,
&error));
g_assert (otcore_get_ostree_target ("blah baz=blah androidboot.slot_suffix=_b somearg", &is_aboot,
&target, &error));
g_assert_no_error (error);
g_assert_cmpstr (target, ==, "/ostree/root.b");
free (g_steal_pointer (&target));

g_assert (otcore_get_ostree_target ("blah baz=blah androidboot.slot_suffix=_a somearg", &target,
&error));
g_assert (otcore_get_ostree_target ("blah baz=blah androidboot.slot_suffix=_a somearg", &is_aboot,
&target, &error));
g_assert_no_error (error);
g_assert_cmpstr (target, ==, "/ostree/root.a");
free (g_steal_pointer (&target));

// And an expected failure to parse a "c" suffix
g_assert (!otcore_get_ostree_target ("blah baz=blah androidboot.slot_suffix=_c somearg", &target,
&error));
g_assert (!otcore_get_ostree_target ("blah baz=blah androidboot.slot_suffix=_c somearg",
&is_aboot, &target, &error));
g_assert (error);
g_assert (target == NULL);
g_clear_error (&error);

// And non-A/B androidboot
g_assert (otcore_get_ostree_target ("blah baz=blah androidboot.somethingelse somearg", &target,
&error));
g_assert (otcore_get_ostree_target ("blah baz=blah androidboot.somethingelse somearg", &is_aboot,
&target, &error));
g_assert_no_error (error);
g_assert_cmpstr (target, ==, "/ostree/root.a");
free (g_steal_pointer (&target));
Expand Down

0 comments on commit 4a71845

Please sign in to comment.