Skip to content

Commit

Permalink
sysroot: Support boot counting for boot entries
Browse files Browse the repository at this point in the history
Add support for boot counting for bootloader entries [1].
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.

All "pending" entries (in the middle of boot counting) are automatically
removed during creation of new boot entries for new deployments.

The feature is enabled via sysroot configuration:
[sysroot]
boot_counting=3

Testing:
$ ostree admin deploy 91fc19319be9e79d07159303dff125f40f10e5c25614630dcbed23d95e36f907
Copying /etc changes: 2 modified, 3 removed, 4 added
bootfs is sufficient for calculated new size: 0 bytes
Transaction complete; bootconfig swap: yes; bootversion: boot.0.1, deployment count change: 1

$ ls /boot/loader/entries
ostree-1+3.conf  ostree-2+3.conf

[1] https://uapi-group.org/specifications/specs/boot_loader_specification/#boot-counting
Signed-off-by: Igor Opaniuk <[email protected]>
  • Loading branch information
igoropaniuk committed Oct 30, 2024
1 parent f7018d8 commit 57e988e
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 9 deletions.
8 changes: 8 additions & 0 deletions man/ostree.repo-config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,14 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
</listitem>
</varlistentry>

<varlistentry>
<term><varname>boot_counting</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.
</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
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", "0",
&boot_counting_str, NULL);

if (boot_counting_str)
/* Ensure boot count value is in [0,5] */
self->boot_counting
= MAX (0, MIN (5, 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
104 changes: 95 additions & 9 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1776,13 +1776,13 @@ parse_os_release (const char *contents, const char *split)
return ret;
}

/* Generate the filename we will use in /boot/loader/entries for this deployment.
/* Generate the entry name 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)
bootloader_entry_name (OstreeSysroot *sysroot, guint n_deployments,
OstreeDeployment *deployment)
{
guint index = n_deployments - ostree_deployment_get_index (deployment);
// Allow opt-out to dropping the stateroot in case of compatibility issues.
Expand All @@ -1792,14 +1792,72 @@ 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);
return g_strdup_printf ("ostree-%d-%s", index, stateroot);
}
else
{
return g_strdup_printf ("ostree-%d.conf", index);
return g_strdup_printf ("ostree-%d", index);
}
}

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

/* Drop all temporary entries in /boot/loader/entries for this deployment,
* which were created during automatic boot assesment
* https://uapi-group.org/specifications/specs/boot_loader_specification/#boot-counting
*/
static gboolean
bootloader_remove_tmp_entries (int dfd, const char *entry_name, guint max_tries, GCancellable *cancellable,
GError **error)
{
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
g_autofree char *entry_name_init = g_strdup_printf ("%s+%u", entry_name, max_tries);

if (!glnx_dirfd_iterator_init_at (dfd, ".", FALSE, &dfd_iter, error))
return FALSE;

while (TRUE)
{
struct dirent *dent = NULL;

if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
return FALSE;
if (dent == NULL)
break;

/* Don't remove default boot entry (with +3 suffix) */
if (g_str_has_prefix (dent->d_name, entry_name_init))
continue;

if (g_str_has_prefix (dent->d_name, entry_name))
{
if (!glnx_shutil_rm_rf_at (dfd_iter.fd, dent->d_name, cancellable, error))
return FALSE;
}

}

return TRUE;
}

/* Given @deployment, prepare it to be booted; basically copying its
* kernel/initramfs into /boot/ostree (if needed) and writing out an entry in
* /boot/loader/entries.
Expand Down Expand Up @@ -1834,7 +1892,7 @@ 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);
g_autofree char *bootconf_name = bootloader_entry_name (sysroot, n_deployments, deployment);

if (!glnx_shutil_mkdir_p_at (sysroot->boot_fd, bootcsumdir, 0775, cancellable, error))
return FALSE;
Expand Down Expand Up @@ -2146,8 +2204,22 @@ 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;
if (bootloader_is_boot_count_enabled(sysroot, cancellable, error))
{
guint max_tries = bootloader_get_max_boot_tries (sysroot, cancellable, error);
bootconf_filename = g_strdup_printf ("%s+%u.conf", bootconf_name, max_tries);

if (!bootloader_remove_tmp_entries(bootconf_dfd, bootconf_name, max_tries, cancellable, error))
return FALSE;
}
else
{
bootconf_filename = g_strdup_printf ("%s.conf", bootconf_name);
}

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 @@ -4176,14 +4248,28 @@ ostree_sysroot_deployment_set_kargs_in_place (OstreeSysroot *self, OstreeDeploym
ostree_bootconfig_parser_set (new_bootconfig, "options", kargs_str);

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

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,
g_autofree char *bootconf_filename;
if (bootloader_is_boot_count_enabled(self, cancellable, error))
{
guint max_tries = bootloader_get_max_boot_tries (self, cancellable, error);
bootconf_filename = g_strdup_printf ("%s+%u.conf", bootconf_name, max_tries);

if (!bootloader_remove_tmp_entries(bootconf_dfd, bootconf_name, max_tries, cancellable, error))
return FALSE;
}
else
{
bootconf_filename = g_strdup_printf ("%s.conf", bootconf_name);
}

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

0 comments on commit 57e988e

Please sign in to comment.