Skip to content

Commit

Permalink
lib/deploy: Add support for additional initrds
Browse files Browse the repository at this point in the history
In FCOS and RHCOS, the need to configure software in the initramfs has
come up multiple times. Sometimes, using kernel arguments suffices.
Other times, it really must be a configuration file. Rebuilding the
initramfs on the client-side however is a costly operation. Not only
does it add complexity to the update workflow, it also erodes a lot of
the value obtained from using the baked "blessed" initramfs from the
tree itself.

One elegant way to address this is to allow specifying multiple
initramfses. This is supported by most bootloaders (notably GRUB) and
results in each initrd being checked out on top of each other.

This patch allows libostree clients to leverage this so that they can
avoid regenerating the initramfs entirely. libostree itself is agnostic
as to what kind and how much data overlay initrds contain. It's up to
the clients to enforce such boundaries.

To implement this, we add a new option to the deploy APIs called
`additional_initrds`: this is an array of filepaths to CPIO archives. We
copy these files into the bootcsum on `/boot` and add them to the BLS as
another `initrd` entry.
  • Loading branch information
jlebon committed Aug 14, 2020
1 parent 72995d1 commit 399c880
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 1 deletion.
6 changes: 6 additions & 0 deletions src/libostree/ostree-deployment-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct _OstreeDeployment
char *csum;
int deployserial;
char *bootcsum;
char **additional_initrds;
int bootserial;
OstreeBootconfigParser *bootconfig;
GKeyFile *origin;
Expand All @@ -56,4 +57,9 @@ struct _OstreeDeployment

void _ostree_deployment_set_bootcsum (OstreeDeployment *self, const char *bootcsum);

void _ostree_deployment_set_additional_initrds (OstreeDeployment *self,
char **additional_initrds);
char**
_ostree_deployment_get_additional_initrds (OstreeDeployment *self);

G_END_DECLS
15 changes: 15 additions & 0 deletions src/libostree/ostree-deployment.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,20 @@ _ostree_deployment_set_bootcsum (OstreeDeployment *self,
self->bootcsum = g_strdup (bootcsum);
}

void
_ostree_deployment_set_additional_initrds (OstreeDeployment *self,
char **additional_initrds)
{
g_strfreev (self->additional_initrds);
self->additional_initrds = g_strdupv (additional_initrds);
}

char**
_ostree_deployment_get_additional_initrds (OstreeDeployment *self)
{
return self->additional_initrds;
}

/**
* ostree_deployment_clone:
* @self: Deployment
Expand Down Expand Up @@ -238,6 +252,7 @@ ostree_deployment_finalize (GObject *object)
g_free (self->bootcsum);
g_clear_object (&self->bootconfig);
g_clear_pointer (&self->origin, g_key_file_unref);
g_strfreev (self->additional_initrds);

G_OBJECT_CLASS (ostree_deployment_parent_class)->finalize (object);
}
Expand Down
43 changes: 43 additions & 0 deletions src/libostree/ostree-sysroot-cleanup.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ cleanup_old_deployments (OstreeSysroot *self,
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_autoptr(GHashTable) active_boot_checksums =
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_autoptr(GHashTable) active_additional_initrds =
g_hash_table_new (g_str_hash, g_str_equal); /* borrows from deployment's bootconfig */
for (guint i = 0; i < self->deployments->len; i++)
{
OstreeDeployment *deployment = self->deployments->pdata[i];
Expand All @@ -306,6 +308,11 @@ cleanup_old_deployments (OstreeSysroot *self,
/* Transfer ownership */
g_hash_table_replace (active_deployment_dirs, deployment_path, deployment_path);
g_hash_table_replace (active_boot_checksums, bootcsum, bootcsum);

OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment);
GPtrArray *initrds = ostree_bootconfig_parser_get_additional_initrds (bootconfig);
for (guint i = 0; i < initrds->len; i++)
g_hash_table_add (active_additional_initrds, (char*)glnx_basename (initrds->pdata[i]));
}

/* Find all deployment directories, both active and inactive */
Expand Down Expand Up @@ -349,6 +356,42 @@ cleanup_old_deployments (OstreeSysroot *self,
return FALSE;
}

/* Clean up additional initrds */
glnx_autofd int overlays_dfd =
glnx_opendirat_with_errno (self->sysroot_fd, _OSTREE_SYSROOT_INITRAMFS_OVERLAYS, FALSE);
if (overlays_dfd < 0)
{
if (errno != ENOENT)
return glnx_throw_errno_prefix (error, "opendirat(overlays)");
}
else
{
g_autoptr(GPtrArray) initrds_to_delete = g_ptr_array_new_with_free_func (g_free);
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
if (!glnx_dirfd_iterator_init_at (overlays_dfd, ".", TRUE, &dfd_iter, error))
return FALSE;
while (TRUE)
{
struct dirent *dent;
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
return FALSE;
if (dent == NULL)
break;

/* there shouldn't be other file types there, but let's be conservative */
if (dent->d_type != DT_REG)
continue;

if (!g_hash_table_lookup (active_additional_initrds, dent->d_name))
g_ptr_array_add (initrds_to_delete, g_strdup (dent->d_name));
}
for (guint i = 0; i < initrds_to_delete->len; i++)
{
if (!ot_ensure_unlinked_at (overlays_dfd, initrds_to_delete->pdata[i], error))
return FALSE;
}
}

return TRUE;
}

Expand Down
52 changes: 52 additions & 0 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1854,6 +1854,48 @@ install_deployment_kernel (OstreeSysroot *sysroot,
}
}

g_autoptr(GPtrArray) additional_initrds = NULL;
for (char **it = _ostree_deployment_get_additional_initrds (deployment); it && *it; it++)
{
char *initrd = *it;

/* Additional initrds are not part of the bootcsum dir; they're not part of the tree
* proper. Instead they're in /boot/ostree/initramfs-overlays/ named by their csum.
* Doing it this way allows sharing the same bootcsum dir for multiple deployments
* with the only change being in additional initrds. */

g_autofree char *checksum =
ot_checksum_file_at (AT_FDCWD, initrd, G_CHECKSUM_SHA256, cancellable, error);
if (checksum == NULL)
return glnx_prefix_error (error, "Checksumming %s", initrd);

g_autofree char *destpath =
g_strdup_printf ("/" _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS "/%s.img", checksum);
const char *rel_destpath = destpath + 1;

/* allocate an array and only create the dir if we actually have initrds */
if (additional_initrds == NULL)
{
additional_initrds = g_ptr_array_new_with_free_func (g_free);

const char *parent = dirname (strdupa (rel_destpath));
if (!glnx_shutil_mkdir_p_at (boot_dfd, parent, 0755, cancellable, error))
return FALSE;
}

if (!glnx_fstatat_allow_noent (boot_dfd, rel_destpath, &stbuf, 0, error))
return FALSE;

if (errno == ENOENT)
{
if (!install_into_boot (repo, sepolicy, AT_FDCWD, initrd, boot_dfd, rel_destpath,
cancellable, error))
return FALSE;
}

g_ptr_array_add (additional_initrds, g_steal_pointer (&destpath));
}

g_autofree char *contents = NULL;
if (!glnx_fstatat_allow_noent (deployment_dfd, "usr/lib/os-release", &stbuf, 0, error))
return FALSE;
Expand Down Expand Up @@ -1933,6 +1975,12 @@ install_deployment_kernel (OstreeSysroot *sysroot,
g_autofree char * initrd_boot_relpath =
g_strconcat ("/", bootcsumdir, "/", kernel_layout->initramfs_namever, NULL);
ostree_bootconfig_parser_set (bootconfig, "initrd", initrd_boot_relpath);

if (additional_initrds)
{
for (guint i = 0; i < additional_initrds->len; i++)
ostree_bootconfig_parser_add_initrd (bootconfig, additional_initrds->pdata[i]);
}
}
else
{
Expand Down Expand Up @@ -2711,6 +2759,7 @@ sysroot_initialize_deployment (OstreeSysroot *self,

_ostree_deployment_set_bootcsum (new_deployment, kernel_layout->bootcsum);
_ostree_deployment_set_bootconfig_from_kargs (new_deployment, opts->override_kernel_argv);
_ostree_deployment_set_additional_initrds (new_deployment, opts->additional_initrds);

if (!prepare_deployment_etc (self, repo, new_deployment, deployment_dfd,
cancellable, error))
Expand Down Expand Up @@ -3111,6 +3160,9 @@ ostree_sysroot_stage_tree_with_options (OstreeSysroot *self,
if (opts->override_kernel_argv)
g_variant_builder_add (builder, "{sv}", "kargs",
g_variant_new_strv ((const char *const*)opts->override_kernel_argv, -1));
if (opts->additional_initrds)
g_variant_builder_add (builder, "{sv}", "additional-initrds",
g_variant_new_strv ((const char *const*)opts->additional_initrds, -1));

const char *parent = dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED));
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, parent, 0755, cancellable, error))
Expand Down
3 changes: 3 additions & 0 deletions src/libostree/ostree-sysroot-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ struct OstreeSysroot {
#define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT "unlocked-development"
#define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_TRANSIENT "unlocked-transient"

#define _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS "ostree/initramfs-overlays"
#define _OSTREE_SYSROOT_INITRAMFS_OVERLAYS "boot/" _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS

gboolean
_ostree_sysroot_ensure_writable (OstreeSysroot *self,
GError **error);
Expand Down
4 changes: 4 additions & 0 deletions src/libostree/ostree-sysroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -966,8 +966,10 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self,
/* Parse it */
g_autoptr(GVariant) target = NULL;
g_autofree char **kargs = NULL;
g_autofree char **additional_initrds = NULL;
g_variant_dict_lookup (staged_deployment_dict, "target", "@a{sv}", &target);
g_variant_dict_lookup (staged_deployment_dict, "kargs", "^a&s", &kargs);
g_variant_dict_lookup (staged_deployment_dict, "additional-initrds", "^a&s", &additional_initrds);
if (target)
{
g_autoptr(OstreeDeployment) staged =
Expand All @@ -979,6 +981,8 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self,
if (!load_origin (self, staged, NULL, error))
return FALSE;

_ostree_deployment_set_additional_initrds (staged, additional_initrds);

self->staged_deployment = g_steal_pointer (&staged);
self->staged_deployment_data = g_steal_pointer (&staged_deployment_data);
/* We set this flag for ostree_deployment_is_staged() because that API
Expand Down
3 changes: 2 additions & 1 deletion src/libostree/ostree-sysroot.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ typedef struct {
gboolean unused_bools[8];
int unused_ints[8];
char **override_kernel_argv;
gpointer unused_ptrs[7];
char **additional_initrds;
gpointer unused_ptrs[6];
} OstreeSysrootDeployTreeOpts;

_OSTREE_PUBLIC
Expand Down

0 comments on commit 399c880

Please sign in to comment.