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
9 changes: 9 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,14 @@ AS_IF([test "$enable_man_html" = yes && test "$enable_man" = no], [
])
AM_CONDITIONAL(ENABLE_MAN_HTML, test "$enable_man_html" = yes)

AC_ARG_ENABLE(boot-count,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think instead of a build-time option this should be a runtime option in the sysroot; something like

[sysroot]
boot-counting = true

or so?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, makes sense, will fix. thanks!

AS_HELP_STRING([--enable-boot-count],
[Enable boot counting for boot entries (default: no)]),,
enable_boot_count=no)
AS_IF([ test "$enable_boot_count" != no], [
AC_DEFINE([ENABLE_BOOT_COUNT], 1, [Define if boot counting is enabled])
])

AC_ARG_WITH(libarchive,
AS_HELP_STRING([--without-libarchive], [Do not use libarchive]),
:, with_libarchive=maybe)
Expand Down Expand Up @@ -705,6 +713,7 @@ echo "
man pages (xsltproc): $enable_man
api docs (gtk-doc): $enable_gtk_doc
installed tests: $enable_installed_tests
boot counting for boot entries: $enable_boot_count
gjs-based tests: $have_gjs
dracut: $with_dracut
mkinitcpio: $with_mkinitcpio
Expand Down
19 changes: 19 additions & 0 deletions src/libostree/ostree-deployment.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,25 @@ ostree_deployment_get_origin_relpath (OstreeDeployment *self)
ostree_deployment_get_deployserial (self));
}


/**
* ostree_deployment_get_boot_tries_relpath:
* @self: A deployment
*
* Note this function only returns a *relative* path - if you want to
* access, it, you must either use fd-relative api such as openat(),
* or concatenate it with the full ostree_sysroot_get_path().
*
* Returns: (not nullable) (transfer full): Path to deployment root directory, relative to sysroot
*/
char *
ostree_deployment_get_boot_tries_relpath (OstreeDeployment *self)
{
return g_strdup_printf ("ostree/deploy/%s/deploy/%s.%d/etc/kernel/tries",
ostree_deployment_get_osname (self), ostree_deployment_get_csum (self),
ostree_deployment_get_deployserial (self));
}

/**
* ostree_deployment_unlocked_state_to_string:
*
Expand Down
3 changes: 3 additions & 0 deletions src/libostree/ostree-deployment.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ OstreeDeployment *ostree_deployment_clone (OstreeDeployment *self);
_OSTREE_PUBLIC
char *ostree_deployment_get_origin_relpath (OstreeDeployment *self);

_OSTREE_PUBLIC
char *ostree_deployment_get_boot_tries_relpath (OstreeDeployment *self);

typedef enum
{
OSTREE_DEPLOYMENT_UNLOCKED_NONE,
Expand Down
99 changes: 90 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,78 @@ 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);
}
}

#ifdef ENABLE_BOOT_COUNT

#define BOOT_COUNT_MAX_RETRIES 3

static gint
bootloader_get_max_boot_tries (OstreeSysroot *self, OstreeDeployment *deployment,
GCancellable *cancellable, GError **error)
{
g_autofree char *tries_file_path = ostree_deployment_get_boot_tries_relpath (deployment);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm...one super messy thing here is that at this point we may not have actually written out the deployment's /etc. We really want a version of this that's in /usr.

AFAICS, the uapi standard doesn't specify /etc/kernel/tries - that's an invention of kernel-install.

To combine with "make this configurable via sysroot" we could have e.g.:

[sysroot]
boot_counting=3

But OTOH I've been trying to get away from the sysroot configuration for other reasons...so we may need to invent /usr/lib/ostree/sysroot.conf or so as a pair with /usr/lib/ostree/prepare-root.conf?


glnx_autofd int fd = -1;
if (!ot_openat_ignore_enoent (self->sysroot_fd, tries_file_path, &fd, error))
return BOOT_COUNT_MAX_RETRIES;
if (fd >= 0)
{
g_autofree char *origin_contents = glnx_fd_readall_utf8 (fd, NULL, cancellable, error);
if (!origin_contents)
return BOOT_COUNT_MAX_RETRIES;

return atoi(origin_contents);
}

return BOOT_COUNT_MAX_RETRIES;
}

/* 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, gint max_tries, GCancellable *cancellable,
GError **error)
{
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
g_autofree char *entry_name_init = g_strdup_printf ("%s+%d", 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;
}
#endif

/* 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 +1898,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 @@ -2145,9 +2209,18 @@ install_deployment_kernel (OstreeSysroot *sysroot, int new_bootversion,
glnx_autofd int bootconf_dfd = -1;
if (!glnx_opendirat (sysroot->boot_fd, bootconfdir, TRUE, &bootconf_dfd, error))
return FALSE;
#ifdef ENABLE_BOOT_COUNT
gint max_tries = bootloader_get_max_boot_tries (sysroot, deployment, cancellable, error);
g_autofree char *bootconf_filename = g_strdup_printf ("%s+%d.conf", bootconf_name, max_tries);

if (!bootloader_remove_tmp_entries(bootconf_dfd, bootconf_name, max_tries, cancellable, error))
return FALSE;
#else
g_autofree char *bootconf_filename = g_strdup_printf ("%s.conf", bootconf_name);
#endif

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 +4249,22 @@ 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;
#ifdef ENABLE_BOOT_COUNT
gint max_tries = bootloader_get_max_boot_tries (self, deployment, cancellable, error);
g_autofree char *bootconf_filename = g_strdup_printf ("%s+%d.conf", bootconf_name, max_tries);

if (!ostree_bootconfig_parser_write_at (new_bootconfig, bootconf_dfd, bootconf_name,
if (!bootloader_remove_tmp_entries(bootconf_dfd, bootconf_name, max_tries, cancellable, error))
return FALSE;
#else
g_autofree char *bootconf_filename = g_strdup_printf ("%s.conf", bootconf_name);
#endif
if (!ostree_bootconfig_parser_write_at (new_bootconfig, bootconf_dfd, bootconf_filename,
cancellable, error))
return FALSE;
}
Expand Down
Loading