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
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
* @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
{
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
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
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;
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);
}
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];
/* 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 */
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;
}
}
}
+ 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;
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
{
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);
_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))
}
+/**
+ * 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
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))
/* 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);
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;
/* 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 =
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
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
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" },
{ "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 }
};
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;
--- /dev/null
+#!/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