diff --git a/src/libostree/ostree-deployment-private.h b/src/libostree/ostree-deployment-private.h index ad77317d79..000c0bbc8d 100644 --- a/src/libostree/ostree-deployment-private.h +++ b/src/libostree/ostree-deployment-private.h @@ -47,6 +47,7 @@ struct _OstreeDeployment char *csum; int deployserial; char *bootcsum; + char **additional_initrds; int bootserial; OstreeBootconfigParser *bootconfig; GKeyFile *origin; @@ -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 diff --git a/src/libostree/ostree-deployment.c b/src/libostree/ostree-deployment.c index 70e1bc4928..6de1f0f43b 100644 --- a/src/libostree/ostree-deployment.c +++ b/src/libostree/ostree-deployment.c @@ -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 @@ -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); } diff --git a/src/libostree/ostree-sysroot-cleanup.c b/src/libostree/ostree-sysroot-cleanup.c index ffad413080..b4a3b4e3d2 100644 --- a/src/libostree/ostree-sysroot-cleanup.c +++ b/src/libostree/ostree-sysroot-cleanup.c @@ -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]; @@ -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 */ @@ -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; } diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 0a5b5001ca..e35b31bb8e 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -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; @@ -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 { @@ -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)) @@ -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)) diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h index fa1e53361c..a3f7ce68a4 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -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); diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index b211fea723..9c7b41522a 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -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 = @@ -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 diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h index 45d6d63cc4..3092b60bd0 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -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