diff --git a/src/switchroot/ostree-mount-util.h b/src/switchroot/ostree-mount-util.h index 92bc802786..c17696a776 100644 --- a/src/switchroot/ostree-mount-util.h +++ b/src/switchroot/ostree-mount-util.h @@ -21,17 +21,19 @@ #ifndef __OSTREE_MOUNT_UTIL_H_ #define __OSTREE_MOUNT_UTIL_H_ +#include #include +#include +#include +#include #include +#include #include -#include #include -#include -#include -#include #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) @@ -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) { @@ -106,6 +128,115 @@ 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. */ diff --git a/src/switchroot/ostree-prepare-root.c b/src/switchroot/ostree-prepare-root.c index a5fbc8a810..45dff125a2 100644 --- a/src/switchroot/ostree-prepare-root.c +++ b/src/switchroot/ostree-prepare-root.c @@ -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/..."); diff --git a/src/switchroot/ostree-system-generator.c b/src/switchroot/ostree-system-generator.c index bd0901bcff..7c7fc0d535 100644 --- a/src/switchroot/ostree-system-generator.c +++ b/src/switchroot/ostree-system-generator.c @@ -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);