diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 4ef396e6..c1d6b35e 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -548,6 +548,7 @@ ostree_sysroot_write_deployments_with_options ostree_sysroot_write_origin_file ostree_sysroot_stage_tree ostree_sysroot_stage_tree_with_options +ostree_sysroot_stage_overlay_initrd ostree_sysroot_deploy_tree ostree_sysroot_deploy_tree_with_options ostree_sysroot_get_merge_deployment diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 3f59c399..341a22a8 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -28,6 +28,7 @@ global: ostree_bootconfig_parser_set_overlay_initrds; ostree_sysroot_deploy_tree_with_options; ostree_sysroot_stage_tree_with_options; + ostree_sysroot_stage_overlay_initrd; } LIBOSTREE_2020.4; /* Stub section for the stable release *after* this development one; don't diff --git a/src/libostree/ostree-deployment-private.h b/src/libostree/ostree-deployment-private.h index ad77317d..b339ae26 100644 --- a/src/libostree/ostree-deployment-private.h +++ b/src/libostree/ostree-deployment-private.h @@ -37,6 +37,8 @@ G_BEGIN_DECLS * @origin: How to construct an upgraded version of this tree * @unlocked: The unlocked state * @staged: TRUE iff this deployment is staged + * @overlay_initrds: Checksums of staged additional initrds for this deployment + * @overlay_initrds_id: Unique ID generated from initrd checksums; used to compare deployments */ struct _OstreeDeployment { @@ -52,8 +54,15 @@ struct _OstreeDeployment GKeyFile *origin; OstreeDeploymentUnlockedState unlocked; gboolean staged; + char **overlay_initrds; + char *overlay_initrds_id; }; void _ostree_deployment_set_bootcsum (OstreeDeployment *self, const char *bootcsum); +void _ostree_deployment_set_overlay_initrds (OstreeDeployment *self, + char **overlay_initrds); + +char** _ostree_deployment_get_overlay_initrds (OstreeDeployment *self); + G_END_DECLS diff --git a/src/libostree/ostree-deployment.c b/src/libostree/ostree-deployment.c index 70e1bc49..182bceea 100644 --- a/src/libostree/ostree-deployment.c +++ b/src/libostree/ostree-deployment.c @@ -158,6 +158,34 @@ _ostree_deployment_set_bootcsum (OstreeDeployment *self, self->bootcsum = g_strdup (bootcsum); } +void +_ostree_deployment_set_overlay_initrds (OstreeDeployment *self, + char **overlay_initrds) +{ + g_clear_pointer (&self->overlay_initrds, g_strfreev); + g_clear_pointer (&self->overlay_initrds_id, g_free); + + if (!overlay_initrds || g_strv_length (overlay_initrds) == 0) + return; + + /* Generate a unique ID representing this combination of overlay initrds. This is so that + * ostree_sysroot_write_deployments_with_options() can easily compare initrds when + * comparing deployments for whether a bootswap is necessary. We could be fancier here but + * meh... this works. */ + g_autoptr(GString) id = g_string_new (NULL); + for (char **it = overlay_initrds; it && *it; it++) + g_string_append (id, *it); + + self->overlay_initrds = g_strdupv (overlay_initrds); + self->overlay_initrds_id = g_string_free (g_steal_pointer (&id), FALSE); +} + +char** +_ostree_deployment_get_overlay_initrds (OstreeDeployment *self) +{ + return self->overlay_initrds; +} + /** * ostree_deployment_clone: * @self: Deployment @@ -175,6 +203,8 @@ ostree_deployment_clone (OstreeDeployment *self) new_bootconfig = ostree_bootconfig_parser_clone (self->bootconfig); ostree_deployment_set_bootconfig (ret, new_bootconfig); + _ostree_deployment_set_overlay_initrds (ret, self->overlay_initrds); + if (self->origin) { g_autoptr(GKeyFile) new_origin = NULL; @@ -238,6 +268,8 @@ 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->overlay_initrds); + g_free (self->overlay_initrds_id); 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 ffad4130..27122834 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_overlay_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); + char **initrds = ostree_bootconfig_parser_get_overlay_initrds (bootconfig); + for (char **it = initrds; it && *it; it++) + g_hash_table_add (active_overlay_initrds, (char*)glnx_basename (*it)); } /* Find all deployment directories, both active and inactive */ @@ -349,6 +356,42 @@ cleanup_old_deployments (OstreeSysroot *self, return FALSE; } + /* Clean up overlay 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, "open(initrd_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_overlay_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 9425316f..1c4fb5dc 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -1859,6 +1859,47 @@ install_deployment_kernel (OstreeSysroot *sysroot, } } + g_autoptr(GPtrArray) overlay_initrds = NULL; + for (char **it = _ostree_deployment_get_overlay_initrds (deployment); it && *it; it++) + { + char *checksum = *it; + + /* Overlay 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 overlay initrds (or conversely, the same overlay + * across different boocsums). Eventually, it'd be nice to have an OSTree repo in + * /boot itself and drop the boocsum dir concept entirely. */ + + g_autofree char *destpath = + g_strdup_printf ("/" _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS "/%s.img", checksum); + const char *rel_destpath = destpath + 1; + + /* lazily allocate array and create dir so we don't pollute /boot if not needed */ + if (overlay_initrds == NULL) + { + overlay_initrds = g_ptr_array_new_with_free_func (g_free); + + if (!glnx_shutil_mkdir_p_at (boot_dfd, _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS, + 0755, cancellable, error)) + return FALSE; + } + + if (!glnx_fstatat_allow_noent (boot_dfd, rel_destpath, NULL, 0, error)) + return FALSE; + if (errno == ENOENT) + { + g_autofree char *srcpath = + g_strdup_printf (_OSTREE_SYSROOT_RUNSTATE_STAGED_INITRDS_DIR "/%s", checksum); + if (!install_into_boot (repo, sepolicy, AT_FDCWD, srcpath, boot_dfd, rel_destpath, + cancellable, error)) + return FALSE; + } + + /* these are used lower down to populate the bootconfig */ + g_ptr_array_add (overlay_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; @@ -1938,6 +1979,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 (overlay_initrds) + { + g_ptr_array_add (overlay_initrds, NULL); + ostree_bootconfig_parser_set_overlay_initrds (bootconfig, (char**)overlay_initrds->pdata); + } } else { @@ -2135,6 +2182,10 @@ deployment_bootconfigs_equal (OstreeRepo *repo, if (strcmp (a_bootcsum, b_bootcsum) != 0) return FALSE; + /* same initrd overlays? */ + if (g_strcmp0 (a->overlay_initrds_id, b->overlay_initrds_id) != 0) + return FALSE; + /* same kargs? */ g_autofree char *a_boot_options_without_ostree = get_deployment_nonostree_kargs (a); g_autofree char *b_boot_options_without_ostree = get_deployment_nonostree_kargs (b); @@ -2722,6 +2773,7 @@ sysroot_initialize_deployment (OstreeSysroot *self, _ostree_deployment_set_bootcsum (new_deployment, kernel_layout->bootcsum); _ostree_deployment_set_bootconfig_from_kargs (new_deployment, opts ? opts->override_kernel_argv : NULL); + _ostree_deployment_set_overlay_initrds (new_deployment, opts ? opts->overlay_initrds : NULL); if (!prepare_deployment_etc (self, repo, new_deployment, deployment_dfd, cancellable, error)) @@ -2991,6 +3043,63 @@ _ostree_sysroot_deserialize_deployment_from_variant (GVariant *v, } +/** + * ostree_sysroot_stage_overlay_initrd: + * @self: Sysroot + * @fd: (transfer none): File descriptor to overlay initrd + * @out_checksum: (out) (transfer full): Overlay initrd checksum + * @cancellable: Cancellable + * @error: Error + * + * Stage an overlay initrd to be used in an upcoming deployment. Returns a checksum which + * can be passed to ostree_sysroot_deploy_tree_with_options() or + * ostree_sysroot_stage_tree_with_options() via the `overlay_initrds` array option. + * + * Since: 2020.7 + */ +gboolean +ostree_sysroot_stage_overlay_initrd (OstreeSysroot *self, + int fd, + char **out_checksum, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (fd != -1, FALSE); + g_return_val_if_fail (out_checksum != NULL, FALSE); + + if (!glnx_shutil_mkdir_p_at (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_INITRDS_DIR, + 0755, cancellable, error)) + return FALSE; + + glnx_autofd int staged_initrds_dfd = -1; + if (!glnx_opendirat (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_INITRDS_DIR, FALSE, + &staged_initrds_dfd, error)) + return FALSE; + + g_auto(GLnxTmpfile) overlay_initrd = { 0, }; + if (!glnx_open_tmpfile_linkable_at (staged_initrds_dfd, ".", O_WRONLY | O_CLOEXEC, + &overlay_initrd, error)) + return FALSE; + + char checksum[_OSTREE_SHA256_STRING_LEN+1]; + { + g_autoptr(GOutputStream) output = g_unix_output_stream_new (overlay_initrd.fd, FALSE); + g_autoptr(GInputStream) input = g_unix_input_stream_new (fd, FALSE); + g_autofree guchar *digest = NULL; + if (!ot_gio_splice_get_checksum (output, input, &digest, cancellable, error)) + return FALSE; + ot_bin2hex (checksum, (guint8*)digest, _OSTREE_SHA256_DIGEST_LEN); + } + + if (!glnx_link_tmpfile_at (&overlay_initrd, GLNX_LINK_TMPFILE_REPLACE, + staged_initrds_dfd, checksum, error)) + return FALSE; + + *out_checksum = g_strdup (checksum); + return TRUE; +} + + /** * ostree_sysroot_stage_tree: * @self: Sysroot @@ -3122,6 +3231,9 @@ ostree_sysroot_stage_tree_with_options (OstreeSysroot *self, if (opts && 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 && opts->overlay_initrds) + g_variant_builder_add (builder, "{sv}", "overlay-initrds", + g_variant_new_strv ((const char *const*)opts->overlay_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 1af2fd27..318b0b19 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -84,10 +84,14 @@ struct OstreeSysroot { /* We keep some transient state in /run */ #define _OSTREE_SYSROOT_RUNSTATE_STAGED "/run/ostree/staged-deployment" #define _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED "/run/ostree/staged-deployment-locked" +#define _OSTREE_SYSROOT_RUNSTATE_STAGED_INITRDS_DIR "/run/ostree/staged-initrds/" #define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_DIR "/run/ostree/deployment-state/" #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 e412ea4d..e0813b55 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -815,6 +815,24 @@ list_deployments_process_one_boot_entry (OstreeSysroot *self, return FALSE; ostree_deployment_set_bootconfig (deployment, config); + char **overlay_initrds = ostree_bootconfig_parser_get_overlay_initrds (config); + g_autoptr(GPtrArray) initrds_chksums = NULL; + for (char **it = overlay_initrds; it && *it; it++) + { + const char *basename = glnx_basename (*it); + if (strlen (basename) != (_OSTREE_SHA256_STRING_LEN + strlen (".img"))) + return glnx_throw (error, "Malformed overlay initrd filename: %s", basename); + + if (!initrds_chksums) /* lazy init */ + initrds_chksums = g_ptr_array_new_full (g_strv_length (overlay_initrds), g_free); + g_ptr_array_add (initrds_chksums, g_strndup (basename, _OSTREE_SHA256_STRING_LEN)); + } + + if (initrds_chksums) + { + g_ptr_array_add (initrds_chksums, NULL); + _ostree_deployment_set_overlay_initrds (deployment, (char**)initrds_chksums->pdata); + } g_ptr_array_add (inout_deployments, g_object_ref (deployment)); return TRUE; @@ -967,8 +985,10 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self, /* Parse it */ g_autoptr(GVariant) target = NULL; g_autofree char **kargs = NULL; + g_autofree char **overlay_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, "overlay-initrds", "^a&s", &overlay_initrds); if (target) { g_autoptr(OstreeDeployment) staged = @@ -980,6 +1000,8 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self, if (!load_origin (self, staged, NULL, error)) return FALSE; + _ostree_deployment_set_overlay_initrds (staged, overlay_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 45d6d63c..3a3b6a77 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -186,11 +186,19 @@ gboolean ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC +gboolean ostree_sysroot_stage_overlay_initrd (OstreeSysroot *self, + int fd, + char **out_checksum, + GCancellable *cancellable, + GError **error); + typedef struct { gboolean unused_bools[8]; int unused_ints[8]; char **override_kernel_argv; - gpointer unused_ptrs[7]; + char **overlay_initrds; + gpointer unused_ptrs[6]; } OstreeSysrootDeployTreeOpts; _OSTREE_PUBLIC diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c index bcece3f6..8156cc15 100644 --- a/src/ostree/ot-admin-builtin-deploy.c +++ b/src/ostree/ot-admin-builtin-deploy.c @@ -44,6 +44,7 @@ static gboolean opt_kernel_proc_cmdline; static char *opt_osname; static char *opt_origin_path; static gboolean opt_kernel_arg_none; +static char **opt_overlay_initrds; static GOptionEntry options[] = { { "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Use a different operating system root than the current one", "OSNAME" }, @@ -59,6 +60,7 @@ static GOptionEntry options[] = { { "karg", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_kernel_argv, "Set kernel argument, like root=/dev/sda1; this overrides any earlier argument with the same name", "NAME=VALUE" }, { "karg-append", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_kernel_argv_append, "Append kernel argument; useful with e.g. console= that can be used multiple times", "NAME=VALUE" }, { "karg-none", 0, 0, G_OPTION_ARG_NONE, &opt_kernel_arg_none, "Do not import kernel arguments", NULL }, + { "overlay-initrd", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_overlay_initrds, "Overlay iniramfs file", "FILE" }, { NULL } }; @@ -167,24 +169,76 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat ostree_kernel_args_append_argv (kargs, opt_kernel_argv_append); } - g_autoptr(OstreeDeployment) new_deployment = NULL; + g_autoptr(GPtrArray) overlay_initrd_chksums = NULL; + for (char **it = opt_overlay_initrds; it && *it; it++) + { + const char *path = *it; + + glnx_autofd int fd = -1; + if (!glnx_openat_rdonly (AT_FDCWD, path, TRUE, &fd, error)) + return FALSE; + + g_autofree char *chksum = NULL; + if (!ostree_sysroot_stage_overlay_initrd (sysroot, fd, &chksum, cancellable, error)) + return FALSE; + + if (!overlay_initrd_chksums) + overlay_initrd_chksums = g_ptr_array_new_full (g_strv_length (opt_overlay_initrds), g_free); + g_ptr_array_add (overlay_initrd_chksums, g_steal_pointer (&chksum)); + } + + if (overlay_initrd_chksums) + g_ptr_array_add (overlay_initrd_chksums, NULL); + g_auto(GStrv) kargs_strv = kargs ? ostree_kernel_args_to_strv (kargs) : NULL; + + OstreeSysrootDeployTreeOpts opts = { + .override_kernel_argv = kargs_strv, + .overlay_initrds = overlay_initrd_chksums ? (char**)overlay_initrd_chksums->pdata : NULL, + }; + + g_autoptr(OstreeDeployment) new_deployment = NULL; if (opt_stage) { if (opt_retain_pending || opt_retain_rollback) return glnx_throw (error, "--stage cannot currently be combined with --retain arguments"); if (opt_not_as_default) return glnx_throw (error, "--stage cannot currently be combined with --not-as-default"); - if (!ostree_sysroot_stage_tree (sysroot, opt_osname, revision, origin, merge_deployment, - kargs_strv, &new_deployment, cancellable, error)) - return FALSE; + /* use old API if we can to exercise it in CI */ + if (!overlay_initrd_chksums) + { + if (!ostree_sysroot_stage_tree (sysroot, opt_osname, revision, origin, + merge_deployment, kargs_strv, &new_deployment, + cancellable, error)) + return FALSE; + } + else + { + if (!ostree_sysroot_stage_tree_with_options (sysroot, opt_osname, revision, + origin, merge_deployment, &opts, + &new_deployment, cancellable, error)) + return FALSE; + } g_assert (new_deployment); } else { - if (!ostree_sysroot_deploy_tree (sysroot, opt_osname, revision, origin, merge_deployment, - kargs_strv, &new_deployment, cancellable, error)) - return FALSE; + /* use old API if we can to exercise it in CI */ + if (!overlay_initrd_chksums) + { + if (!ostree_sysroot_deploy_tree (sysroot, opt_osname, revision, origin, + merge_deployment, kargs_strv, &new_deployment, + cancellable, error)) + return FALSE; + } + else + { + if (!ostree_sysroot_deploy_tree_with_options (sysroot, opt_osname, revision, + origin, merge_deployment, &opts, + &new_deployment, cancellable, + error)) + return FALSE; + } g_assert (new_deployment); OstreeSysrootSimpleWriteDeploymentFlags flags = OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN; diff --git a/tests/kolainst/destructive/overlay-initrds.sh b/tests/kolainst/destructive/overlay-initrds.sh new file mode 100755 index 00000000..b24d2d08 --- /dev/null +++ b/tests/kolainst/destructive/overlay-initrds.sh @@ -0,0 +1,96 @@ +#!/bin/bash +# https://github.com/ostreedev/ostree/issues/1667 +set -xeuo pipefail + +. ${KOLA_EXT_DATA}/libinsttest.sh + +# we don't just use `rpm-ostree initramfs-etc` here because we want to be able +# to test more things + +# dracut prints all the cmdline args, including those from /etc/cmdline.d, so +# the way we test that an initrd was included is to just add kargs there and +# grep for it +create_initrd_with_dracut_karg() { + local karg=$1; shift + local d + d=$(mktemp -dp /var/tmp) + mkdir -p "${d}/etc/cmdline.d" + echo "${karg}" > "${d}/etc/cmdline.d/${karg}.conf" + echo "etc/cmdline.d/${karg}.conf" | \ + cpio -D "${d}" -o -H newc --reproducible > "/var/tmp/${karg}.img" +} + +check_for_dracut_karg() { + local karg=$1; shift + # https://github.com/dracutdevs/dracut/blob/38ea7e821b/modules.d/98dracut-systemd/dracut-cmdline.sh#L17 + journalctl -b 0 -t dracut-cmdline \ + --grep "Using kernel command line parameters:.* ${karg} " +} + +case "${AUTOPKGTEST_REBOOT_MARK:-}" in + "") + create_initrd_with_dracut_karg ostree.test1 + # let's use the deploy API first + ostree admin deploy "${host_refspec}" \ + --overlay-initrd /var/tmp/ostree.test1.img + /tmp/autopkgtest-reboot "2" + ;; + 2) + # verify that ostree.test1 is here + check_for_dracut_karg ostree.test1 + img_sha=$(sha256sum < /var/tmp/ostree.test1.img | cut -f 1 -d ' ') + test -f "/boot/ostree/initramfs-overlays/${img_sha}.img" + + # now let's change to ostree.test2 + create_initrd_with_dracut_karg ostree.test2 + + # let's use the staging API this time + ostree admin deploy "${host_refspec}" --stage \ + --overlay-initrd /var/tmp/ostree.test2.img + /tmp/autopkgtest-reboot "3" + ;; + 3) + # verify that ostree.test1 is gone, but ostree.test2 is here + if check_for_dracut_karg ostree.test1; then + assert_not_reached "Unexpected ostree.test1 karg found" + fi + check_for_dracut_karg ostree.test2 + + # both the new and old initrds should still be there since they're + # referenced in the BLS + test1_sha=$(sha256sum < /var/tmp/ostree.test1.img | cut -f 1 -d ' ') + test2_sha=$(sha256sum < /var/tmp/ostree.test2.img | cut -f 1 -d ' ') + test -f "/boot/ostree/initramfs-overlays/${test1_sha}.img" + test -f "/boot/ostree/initramfs-overlays/${test2_sha}.img" + + # OK, now let's deploy an identical copy of this test + ostree admin deploy "${host_refspec}" \ + --overlay-initrd /var/tmp/ostree.test2.img + + # Now the deployment with ostree.test1 should've been GC'ed; check that its + # initrd was cleaned up + test ! -f "/boot/ostree/initramfs-overlays/${test1_sha}.img" + test -f "/boot/ostree/initramfs-overlays/${test2_sha}.img" + + # deploy again to check that no bootconfig swap was needed; this verifies + # that deployment overlay initrds can be successfully compared + ostree admin deploy "${host_refspec}" \ + --overlay-initrd /var/tmp/ostree.test2.img |& tee /tmp/out.txt + assert_file_has_content /tmp/out.txt 'bootconfig swap: no' + + # finally, let's check that we can overlay multiple initrds + ostree admin deploy "${host_refspec}" --stage \ + --overlay-initrd /var/tmp/ostree.test1.img \ + --overlay-initrd /var/tmp/ostree.test2.img + /tmp/autopkgtest-reboot "4" + ;; + 4) + check_for_dracut_karg ostree.test1 + check_for_dracut_karg ostree.test2 + test1_sha=$(sha256sum < /var/tmp/ostree.test1.img | cut -f 1 -d ' ') + test2_sha=$(sha256sum < /var/tmp/ostree.test2.img | cut -f 1 -d ' ') + test -f "/boot/ostree/initramfs-overlays/${test1_sha}.img" + test -f "/boot/ostree/initramfs-overlays/${test2_sha}.img" + ;; + *) fatal "Unexpected AUTOPKGTEST_REBOOT_MARK=${AUTOPKGTEST_REBOOT_MARK}" ;; +esac