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 Apr 28, 2023
1 parent b4dcfd7 commit 61353f3
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 6 deletions.
142 changes: 138 additions & 4 deletions src/switchroot/ostree-mount-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@
#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 <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 +74,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 +128,118 @@ read_proc_cmdline_ostree (void)
return ret;
}

/* If the key matches the start of the line, strip the key from the line just
* to get the value
*/
static inline bool
rm_key_if_match (char *line, const char *key, const size_t key_len, char *out)
{
if (strstr (line, key) == line)
{
char *value = line + key_len;
strcpy (out, value);
return true;
}

return false;
}

/* 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 int
copy_if_higher_version (FILE *f, char *version, char *options)
{
char __attribute__ ((cleanup (free_char))) *line = NULL;
char version_local[PATH_MAX] = "";
char options_local[PATH_MAX] = "";
/* Note getline() will reuse the previous buffer */
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 (rm_key_if_match (line, "version ", sizeof ("version"),
version_local))
{
if (options_local[0])
break;

continue;
}

if (rm_key_if_match (line, "options ", sizeof ("options"),
options_local))
{
if (version_local[0])
break;

continue;
}
}

const int ret = strverscmp (version_local, version);
if (ret > 0)
{
strcpy (version, version_local);
strcpy (options, options_local);
}

return ret;
}

/* 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 strdup ("");
}

char version[PATH_MAX] = "";
char options[PATH_MAX] = "";
for (struct dirent *ent = 0; (ent = readdir (dir));)
{
if (ent->d_name[0] == '.')
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);
}

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

return strdup ("");
}

/* 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
6 changes: 5 additions & 1 deletion src/switchroot/ostree-system-generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ 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 61353f3

Please sign in to comment.