Merge 7e4fe416c2d21147038466b180b5e6209d03665d into ce4e49e1f44b21b3cdea6fa41e2b047d70a6d70b

This commit is contained in:
Igor Opaniuk 2025-01-02 15:35:42 +01:00 committed by GitHub
commit a2c7ce1f3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 178 additions and 8 deletions

View File

@ -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:

View File

@ -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. */
@ -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
@ -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;

View File

@ -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

View File

@ -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;
};

View File

@ -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] */
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",

View File

@ -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>
@ -1806,14 +1807,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);
}
/* 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
@ -1822,12 +1845,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
@ -1864,7 +1903,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;
@ -2178,8 +2216,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;
@ -4217,15 +4258,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;
}