Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sysroot: Support boot counting for boot entries #3310

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions man/ostree.repo-config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,18 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
</listitem>
</varlistentry>

<varlistentry>
<term><varname>boot-counting-tries</varname></term>
<listitem><para>Integer value controlling the number of maximum boot attempts. The boot
counting data is stored in the name of the boot loader entry. A boot loader entry file name
may contain a plus (+) followed by a number. This may optionally be followed by
a minus (-) followed by a second number. The dot (.) and file name suffix (conf or efi) must
immediately follow. More details in the
<ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification/#boot-counting">
The Boot Loader Specification</ulink>
</para></listitem>
</varlistentry>

<varlistentry>
<term><varname>bls-append-except-default</varname></term>
<listitem><para>A semicolon separated string list of key-value pairs. For example:
Expand Down
93 changes: 93 additions & 0 deletions src/libostree/ostree-bootconfig-parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ struct _OstreeBootconfigParser
gboolean parsed;
const char *separators;

guint64 tries_left;
guint64 tries_done;

GHashTable *options;

/* Additional initrds; the primary initrd is in options. */
Expand Down Expand Up @@ -56,6 +59,94 @@ ostree_bootconfig_parser_clone (OstreeBootconfigParser *self)
return parser;
}

/**
* ostree_bootconfig_parser_parse_tries:
* @self: Parser
* @path: File path
*
* Parses a suffix of two counters in the form "+LEFT-DONE" from the end of the
* filename (excluding file extension).
*/
static void
ostree_bootconfig_parser_parse_tries (OstreeBootconfigParser *self, const char *filename)
{
gchar *counter = NULL;
gchar *old_counter = NULL;
self->tries_left = 0;
self->tries_done = 0;

for (;;)
{
char *plus = strchr (counter ?: filename, '+');
if (plus)
{
/* Look for the last "+" symbol in the filename */
counter = plus + 1;
continue;
}
if (counter)
break;

/* No boot counter found */
return;
}

guint64 tries_left, tries_done = 0;

old_counter = counter;
tries_left = g_ascii_strtoull (old_counter, &counter, 10);
if ((old_counter == counter) || (tries_left > INT_MAX))
return;

/* Parse done counter only if present */
if (*counter == '-')
{
old_counter = counter;
tries_done = g_ascii_strtoull (counter + 1, &counter, 10);
if ((old_counter == counter) || (tries_left > INT_MAX))
return;
}

self->tries_left = tries_left;
self->tries_done = tries_done;
}

/**
* ostree_bootconfig_parser_get_tries_left:
* @self: Parser
*
* Returns: Amount of boot tries left
*/
guint64
ostree_bootconfig_parser_get_tries_left (OstreeBootconfigParser *self)
{
return self->tries_left;
}

/**
* ostree_bootconfig_parser_get_tries_done:
* @self: Parser
*
* Returns: Amount of boot tries
*/
guint64
ostree_bootconfig_parser_get_tries_done (OstreeBootconfigParser *self)
{
return self->tries_done;
}

/**
* ostree_bootconfig_parser_get_tries_done:
* @self: Parser
*
* Returns: TRUE if the boot config was parsed from existing boot config file
*/
gboolean
ostree_bootconfig_parser_is_parsed (OstreeBootconfigParser *self)
{
return self->parsed;
}

/**
* ostree_bootconfig_parser_parse_at:
* @self: Parser
Expand Down Expand Up @@ -116,6 +207,8 @@ ostree_bootconfig_parser_parse_at (OstreeBootconfigParser *self, int dfd, const
self->overlay_initrds = (char **)g_ptr_array_free (g_steal_pointer (&overlay_initrds), FALSE);
}

ostree_bootconfig_parser_parse_tries(self, glnx_basename(path));

self->parsed = TRUE;

return TRUE;
Expand Down
9 changes: 9 additions & 0 deletions src/libostree/ostree-bootconfig-parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,13 @@ void ostree_bootconfig_parser_set_overlay_initrds (OstreeBootconfigParser *self,
_OSTREE_PUBLIC
char **ostree_bootconfig_parser_get_overlay_initrds (OstreeBootconfigParser *self);

_OSTREE_PUBLIC
guint64 ostree_bootconfig_parser_get_tries_left (OstreeBootconfigParser *self);

_OSTREE_PUBLIC
guint64 ostree_bootconfig_parser_get_tries_done (OstreeBootconfigParser *self);

_OSTREE_PUBLIC
gboolean ostree_bootconfig_parser_is_parsed (OstreeBootconfigParser *self);

G_END_DECLS
1 change: 1 addition & 0 deletions src/libostree/ostree-repo-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ struct OstreeRepo
GHashTable
*bls_append_values; /* Parsed key-values from bls-append-except-default key in config. */
gboolean enable_bootprefix; /* If true, prepend bootloader entries with /boot */
guint boot_counting;

OstreeRepo *parent_repo;
};
Expand Down
14 changes: 14 additions & 0 deletions src/libostree/ostree-repo.c
Original file line number Diff line number Diff line change
Expand Up @@ -3297,6 +3297,20 @@ reload_remote_config (OstreeRepo *self, GCancellable *cancellable, GError **erro
static gboolean
reload_sysroot_config (OstreeRepo *self, GCancellable *cancellable, GError **error)
{
{
g_autofree char *boot_counting_str = NULL;

(void)ot_keyfile_get_value_with_default_group_optional (self->config, "sysroot", "boot-counting-tries", "0",
&boot_counting_str, NULL);

if (boot_counting_str)
/* Ensure boot count value is in [0,5] */
igoropaniuk marked this conversation as resolved.
Show resolved Hide resolved
self->boot_counting
= MAX (0, MIN (INT_MAX, g_ascii_strtoull (boot_counting_str, NULL, 10)));
else
self->boot_counting = 0;
}

g_autofree char *bootloader = NULL;

if (!ot_keyfile_get_value_with_default_group_optional (self->config, "sysroot", "bootloader",
Expand Down
57 changes: 49 additions & 8 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <gio/gunixinputstream.h>
#include <gio/gunixoutputstream.h>
#include <glib-unix.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/ioctl.h>
Expand Down Expand Up @@ -1801,14 +1802,36 @@ parse_os_release (const char *contents, const char *split)
return ret;
}

static guint
bootloader_get_max_boot_tries (OstreeSysroot *self, GCancellable *cancellable, GError **error)
{
g_autoptr (OstreeRepo) repo = NULL;
if (!ostree_sysroot_get_repo (self, &repo, cancellable, error))
return 0;

return repo->boot_counting;
}

static gboolean
bootloader_is_boot_count_enabled (OstreeSysroot *self, GCancellable *cancellable, GError **error)
{
g_autoptr (OstreeRepo) repo = NULL;
if (!ostree_sysroot_get_repo (self, &repo, cancellable, error))
return FALSE;

return (repo->boot_counting != 0 ? TRUE : FALSE);
igoropaniuk marked this conversation as resolved.
Show resolved Hide resolved
}

/* Generate the filename we will use in /boot/loader/entries for this deployment.
* The provided n_deployments should be the total number of target deployments (which
* might be different from the cached value in the sysroot).
*/
static char *
bootloader_entry_filename (OstreeSysroot *sysroot, guint n_deployments,
OstreeDeployment *deployment)
OstreeDeployment *deployment, GCancellable *cancellable,
GError **error)
{
g_autofree char *bootconf_name;
guint index = n_deployments - ostree_deployment_get_index (deployment);
// Allow opt-out to dropping the stateroot in case of compatibility issues.
// As of 2024.5, we have a new naming scheme because grub2 parses the *filename* and ignores
Expand All @@ -1817,12 +1840,28 @@ bootloader_entry_filename (OstreeSysroot *sysroot, guint n_deployments,
if (use_old_naming)
{
const char *stateroot = ostree_deployment_get_osname (deployment);
return g_strdup_printf ("ostree-%d-%s.conf", index, stateroot);
bootconf_name = g_strdup_printf ("ostree-%d-%s", index, stateroot);
}
else
{
return g_strdup_printf ("ostree-%d.conf", index);
bootconf_name = g_strdup_printf ("ostree-%d", index);
}

if (!bootloader_is_boot_count_enabled(sysroot, cancellable, error))
return g_strdup_printf ("%s.conf", bootconf_name);

guint max_tries = bootloader_get_max_boot_tries (sysroot, cancellable, error);
OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment);

if (!ostree_bootconfig_parser_is_parsed (bootconfig))
return g_strdup_printf ("%s+%u.conf", bootconf_name, max_tries);
else if (!ostree_bootconfig_parser_get_tries_left (bootconfig)
&& !ostree_bootconfig_parser_get_tries_done (bootconfig))
return g_strdup_printf ("%s.conf", bootconf_name);
else
return g_strdup_printf ("%s+%" PRIu64 "-%" PRIu64 ".conf", bootconf_name,
ostree_bootconfig_parser_get_tries_left (bootconfig),
ostree_bootconfig_parser_get_tries_done (bootconfig));
}

/* Given @deployment, prepare it to be booted; basically copying its
Expand Down Expand Up @@ -1859,7 +1898,6 @@ install_deployment_kernel (OstreeSysroot *sysroot, int new_bootversion,
const char *bootcsum = ostree_deployment_get_bootcsum (deployment);
g_autofree char *bootcsumdir = g_strdup_printf ("ostree/%s-%s", osname, bootcsum);
g_autofree char *bootconfdir = g_strdup_printf ("loader.%d/entries", new_bootversion);
g_autofree char *bootconf_name = bootloader_entry_filename (sysroot, n_deployments, deployment);

if (!glnx_shutil_mkdir_p_at (sysroot->boot_fd, bootcsumdir, 0775, cancellable, error))
return FALSE;
Expand Down Expand Up @@ -2173,8 +2211,11 @@ install_deployment_kernel (OstreeSysroot *sysroot, int new_bootversion,
if (!glnx_opendirat (sysroot->boot_fd, bootconfdir, TRUE, &bootconf_dfd, error))
return FALSE;

g_autofree char *bootconf_filename = bootloader_entry_filename (sysroot, n_deployments, deployment,
cancellable, error);

if (!ostree_bootconfig_parser_write_at (ostree_deployment_get_bootconfig (deployment),
bootconf_dfd, bootconf_name, cancellable, error))
bootconf_dfd, bootconf_filename, cancellable, error))
return FALSE;

return TRUE;
Expand Down Expand Up @@ -4212,15 +4253,15 @@ ostree_sysroot_deployment_set_kargs_in_place (OstreeSysroot *self, OstreeDeploym
OstreeBootconfigParser *new_bootconfig = ostree_deployment_get_bootconfig (deployment);
ostree_bootconfig_parser_set (new_bootconfig, "options", kargs_str);

g_autofree char *bootconf_name
= bootloader_entry_filename (self, self->deployments->len, deployment);
g_autofree char *bootconf_filename
= bootloader_entry_filename (self, self->deployments->len, deployment, cancellable, error);

g_autofree char *bootconfdir = g_strdup_printf ("loader.%d/entries", self->bootversion);
glnx_autofd int bootconf_dfd = -1;
if (!glnx_opendirat (self->boot_fd, bootconfdir, TRUE, &bootconf_dfd, error))
return FALSE;

if (!ostree_bootconfig_parser_write_at (new_bootconfig, bootconf_dfd, bootconf_name,
if (!ostree_bootconfig_parser_write_at (new_bootconfig, bootconf_dfd, bootconf_filename,
cancellable, error))
return FALSE;
}
Expand Down
Loading