From 6b334245495c018792f36eece8d7a91cfe290527 Mon Sep 17 00:00:00 2001 From: Igor Opaniuk Date: Wed, 11 Sep 2024 18:03:10 +0200 Subject: [PATCH] sysroot: Support boot counting for boot entries 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. 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 --- src/libostree/ostree-sysroot-deploy.c | 66 +++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index d52eecf3de..1f6843d8e2 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -62,6 +62,8 @@ SD_ID128_MAKE (e8, 64, 6c, d6, 3d, ff, 46, 25, b7, 79, 09, a8, e7, a4, 09, 94) #endif +#define BOOT_COUNT_MAX_RETRIES 3 + /* How much additional space we require available on top of what we accounted * during the early prune fallocate space check. This accounts for anything not * captured directly by `get_kernel_layout_size()` like writing new BLS entries. @@ -1776,12 +1778,12 @@ 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, +bootloader_entry_name (OstreeSysroot *sysroot, guint n_deployments, OstreeDeployment *deployment) { guint index = n_deployments - ostree_deployment_get_index (deployment); @@ -1792,12 +1794,50 @@ 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); + } +} + +/* Drop all temporary entries in /boot/loader/entries for this deployment, + * which were created during automatic boot assesment + * https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/ + */ +static gboolean +bootloader_remove_tmp_entries (int dfd, const char *entry_name, GCancellable *cancellable, + GError **error) +{ + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + g_autofree char *entry_name_init = g_strdup_printf ("%s+%d", entry_name, BOOT_COUNT_MAX_RETRIES); + + 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 @@ -1834,7 +1874,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; @@ -2146,8 +2186,13 @@ 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 = g_strdup_printf ("%s+%d.conf", bootconf_name, BOOT_COUNT_MAX_RETRIES); + + if (!bootloader_remove_tmp_entries(bootconf_dfd, bootconf_name, cancellable, error)) + return FALSE; + 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; @@ -4176,14 +4221,19 @@ 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 = g_strdup_printf ("%s+%d.conf", bootconf_name, BOOT_COUNT_MAX_RETRIES); + + if (!bootloader_remove_tmp_entries(bootconf_dfd, bootconf_name, cancellable, error)) + return FALSE; + + if (!ostree_bootconfig_parser_write_at (new_bootconfig, bootconf_dfd, bootconf_filename, cancellable, error)) return FALSE; }