Skip to content

Commit

Permalink
Add ostree=aboot for signed Android Boot Images
Browse files Browse the repository at this point in the history
Some kernel images are delivered in a signed kernel + cmdline +
initramfs + dtb blob. When this is added to the commit server side, only
after this do you know what the cmdline is, this creates a recursion
issue. To avoid this, in the case where we have ostree=aboot karg
set, do the bls parsing in the initramfs instead, so we can take
advantage of existing bls logic.
  • Loading branch information
ericcurtin committed May 2, 2023
1 parent dd70c9b commit 03aa8f7
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 6 deletions.
175 changes: 171 additions & 4 deletions src/switchroot/ostree-mount-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,20 @@
#ifndef __OSTREE_MOUNT_UTIL_H_
#define __OSTREE_MOUNT_UTIL_H_

#include <dirent.h>
#include <err.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
#include <stdio.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdbool.h>

#define INITRAMFS_MOUNT_VAR "/run/ostree/initramfs-mount-var"
#define _OSTREE_SYSROOT_READONLY_STAMP "/run/ostree-sysroot-ro.stamp"
#define ABOOT_KARG "aboot"

static inline int
path_is_on_readonly_fs (const char *path)
Expand Down Expand Up @@ -72,6 +75,26 @@ read_proc_cmdline (void)
return cmdline;
}

static inline void
free_char (char **to_free)
{
free (*to_free);
}

static inline void
close_dir (DIR **dir)
{
if (*dir)
closedir (*dir);
}

static inline void
close_file (FILE **f)
{
if (*f)
fclose (*f);
}

static inline char *
read_proc_cmdline_ostree (void)
{
Expand Down Expand Up @@ -106,6 +129,150 @@ read_proc_cmdline_ostree (void)
return ret;
}

static inline void
cpy_and_null (char **dest, char **src)
{
*dest = *src;
*src = NULL;
}

/* If the key matches the start of the line, copy line to out
*/
static inline bool
cpy_if_key_match (char **line, const char *key, char **out)
{
/* There should only be one of each key per BLS file, so check for NULL, we
* should only parse the first occurance of a key, if there's two, it's a
* malformed BLS file
*/
if (!*out && strstr (*line, key) == *line)
{
cpy_and_null (out, line);
return true;
}

return false;
}

static inline bool
has_suffix (const char *str, const char *suffix)
{
if (!str || !suffix)
return false;

const size_t str_len = strlen (str);
const size_t suffix_len = strlen (suffix);
if (str_len < suffix_len)
return false;

return !strcmp (str + str_len - suffix_len, suffix);
}

/* On completion version and options will be set to new values if the version
* is more recent. Will loop line through line on the passed in open FILE.
*/
static inline void
copy_if_higher_version (FILE *f, char **version, char **options)
{
char __attribute__ ((cleanup (free_char))) *line = NULL;
char __attribute__ ((cleanup (free_char))) *version_local = NULL;
char __attribute__ ((cleanup (free_char))) *options_local = NULL;
char __attribute__ ((cleanup (free_char))) *linux_local = NULL;
/* Note getline() will reuse the previous buffer when not zero */
for (size_t len = 0; getline (&line, &len, f) != -1;)
{
/* This is an awful hack to avoid depending on GLib in the
* initramfs right now.
*/
if (cpy_if_key_match (&line, "version ", &version_local))
continue;

if (cpy_if_key_match (&line, "options ", &options_local))
continue;

if (cpy_if_key_match (&line, "linux ", &linux_local))
continue;
}

/* The case where we have no version set yet */
if (!*version
|| strverscmp (version_local + sizeof ("version"), (*version) + sizeof ("version")) > 0)
{
struct utsname buf;
uname (&buf);
strtok (linux_local + sizeof ("linux"), " \t\r\n");
if (!has_suffix (linux_local, buf.release))
return;

cpy_and_null (version, &version_local);
cpy_and_null (options, &options_local);
}

return;
}

static inline char *
parse_ostree_from_options (const char *options)
{
if (options)
{
options += sizeof ("options");
char *start_of_ostree = strstr (options, "ostree=");
if (start_of_ostree > options)
{
start_of_ostree += sizeof ("ostree");
/* trim everything to the right */
strtok (start_of_ostree, " \t\r\n");
return strdup (start_of_ostree);
}
}

return NULL;
}

/* This function is for boot arrangements where it is not possible to use a
* karg/cmdline, this is the case when the cmdline is part of the signed
* boot image, alternatively this function takes the karg from the bls entry
* which will have the correct ostree= karg set. This bls entry is not parsed
* from the bootloader but from the initramfs instead.
*/
static inline char *
bls_parser_get_ostree_option (const char *sysroot)
{
char out[PATH_MAX] = "";
int written = snprintf (out, PATH_MAX, "%s/boot/loader/entries", sysroot);
DIR __attribute__ ((cleanup (close_dir))) *dir = opendir (out);
if (!dir)
{
fprintf (stderr, "opendir(\"%s\") failed with %d\n", out, errno);
return NULL;
}

char __attribute__ ((cleanup (free_char))) *version = NULL;
char __attribute__ ((cleanup (free_char))) *options = NULL;
for (struct dirent *ent = 0; (ent = readdir (dir));)
{
if (ent->d_name[0] == '.')
continue;

if (!has_suffix (ent->d_name, ".conf"))
continue;

snprintf (out + written, PATH_MAX - written, "/%s", ent->d_name);

FILE __attribute__ ((cleanup (close_file))) *f = fopen (out, "r");
if (!f)
{
fprintf (stderr, "fopen(\"%s\", \"r\") failed with %d\n", out, errno);
continue;
}

copy_if_higher_version (f, &version, &options);
}

return parse_ostree_from_options (options);
}

/* This is an API for other projects to determine whether or not the
* currently running system is ostree-controlled.
*/
Expand Down
6 changes: 5 additions & 1 deletion src/switchroot/ostree-prepare-root.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,13 @@ resolve_deploy_path (const char * root_mountpoint)
{
char destpath[PATH_MAX];
struct stat stbuf;
char *ostree_target, *deploy_path;
char __attribute__ ((cleanup (free_char))) *ostree_target;
char *deploy_path;

ostree_target = read_proc_cmdline_ostree ();
if (!strcmp (ostree_target, ABOOT_KARG))
ostree_target = bls_parser_get_ostree_option (root_mountpoint);

if (!ostree_target)
errx (EXIT_FAILURE, "No OSTree target; expected ostree=/ostree/boot.N/...");

Expand Down
5 changes: 4 additions & 1 deletion src/switchroot/ostree-system-generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ main(int argc, char *argv[])
* exit so that we don't error, but at the same time work where switchroot
* is PID 1 (and so hasn't created /run/ostree-booted).
*/
char *ostree_cmdline = read_proc_cmdline_ostree ();
char __attribute__ ((cleanup (free_char))) *ostree_cmdline = read_proc_cmdline_ostree ();
if (!strcmp (ostree_cmdline, ABOOT_KARG))
ostree_cmdline = bls_parser_get_ostree_option ("/sysroot");

if (!ostree_cmdline)
exit (EXIT_SUCCESS);

Expand Down

0 comments on commit 03aa8f7

Please sign in to comment.