diff --git a/Makefile-boot.am b/Makefile-boot.am
index ec10a0d64..79de63e24 100644
--- a/Makefile-boot.am
+++ b/Makefile-boot.am
@@ -40,6 +40,7 @@ systemdsystemunit_DATA = src/boot/ostree-prepare-root.service \
src/boot/ostree-remount.service \
src/boot/ostree-finalize-staged.service \
src/boot/ostree-finalize-staged.path \
+ src/boot/ostree-finalize-staged-hold.service \
$(NULL)
systemdtmpfilesdir = $(prefix)/lib/tmpfiles.d
dist_systemdtmpfiles_DATA = src/boot/ostree-tmpfiles.conf
@@ -68,6 +69,7 @@ EXTRA_DIST += src/boot/dracut/module-setup.sh \
src/boot/ostree-finalize-staged.path \
src/boot/ostree-remount.service \
src/boot/ostree-finalize-staged.service \
+ src/boot/ostree-finalize-staged-hold.service \
src/boot/grub2/grub2-15_ostree \
src/boot/grub2/ostree-grub-generator \
$(NULL)
diff --git a/libglnx b/libglnx
index ef502aabf..07e3e49d3 160000
--- a/libglnx
+++ b/libglnx
@@ -1 +1 @@
-Subproject commit ef502aabf7d3a0d37f9c4d228f870ac93404447b
+Subproject commit 07e3e49d3e47dfd4265ffb5495111439131715ca
diff --git a/man/ostree-summary.xml b/man/ostree-summary.xml
index 4c9652ccb..e853e8cdf 100644
--- a/man/ostree-summary.xml
+++ b/man/ostree-summary.xml
@@ -183,6 +183,7 @@ License along with this library. If not, see .
Latest Commit (4.2 MB):
9828ab80f357459b4ab50f0629beab2ae3b67318fc3d161d10a89fae353afa90
Timestamp (ostree.commit.timestamp): 2017-11-21T01:41:10-08
+ Version (ostree.commit.version): 1.2.3
Last-Modified (ostree.summary.last-modified): 2018-01-12T22:06:38-08
diff --git a/src/boot/ostree-finalize-staged-hold.service b/src/boot/ostree-finalize-staged-hold.service
new file mode 100644
index 000000000..85b5d543b
--- /dev/null
+++ b/src/boot/ostree-finalize-staged-hold.service
@@ -0,0 +1,35 @@
+# Copyright (C) 2018 Red Hat, Inc.
+# Copyright (C) 2022 Endless OS Foundation LLC
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see .
+
+# See https://github.com/ostreedev/ostree/pull/2543 for background.
+[Unit]
+Description=Hold /boot Open for OSTree Finalize Staged Deployment
+Documentation=man:ostree(1)
+ConditionPathExists=/run/ostree-booted
+DefaultDependencies=no
+
+RequiresMountsFor=/sysroot /boot
+After=local-fs.target
+Before=basic.target final.target
+
+[Service]
+Type=exec
+
+# This is explicitly run in the root namespace to ensure an automounted
+# /boot doesn't time out since autofs doesn't handle mount namespaces.
+#
+# https://bugzilla.redhat.com/show_bug.cgi?id=2056090
+ExecStart=+/usr/bin/ostree admin finalize-staged --hold
diff --git a/src/boot/ostree-finalize-staged.service b/src/boot/ostree-finalize-staged.service
index 2f28bbb70..63621ce19 100644
--- a/src/boot/ostree-finalize-staged.service
+++ b/src/boot/ostree-finalize-staged.service
@@ -29,6 +29,11 @@ Before=basic.target final.target
After=systemd-journal-flush.service
Conflicts=final.target
+# Start the hold unit and ensure it stays active throughout this
+# service.
+Wants=ostree-finalize-staged-hold.service
+After=ostree-finalize-staged-hold.service
+
[Service]
Type=oneshot
RemainAfterExit=yes
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
index 36e612909..48a75f92a 100644
--- a/src/libostree/ostree-core.h
+++ b/src/libostree/ostree-core.h
@@ -164,6 +164,8 @@ typedef enum {
* The currently defined keys for the `a{sv}` of additional metadata for each commit are:
* - key: `ostree.commit.timestamp`, value: `t`, timestamp (seconds since the
* Unix epoch in UTC, big-endian) when the commit was committed
+ * - key: `ostree.commit.version`, value: `s`, the `version` value from the
+ * commit's metadata if it was defined. Since: 2022.2
*/
#define OSTREE_SUMMARY_GVARIANT_STRING "(a(s(taya{sv}))a{sv})"
#define OSTREE_SUMMARY_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_SUMMARY_GVARIANT_STRING)
diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h
index 6d8f0193b..988c2179a 100644
--- a/src/libostree/ostree-repo-private.h
+++ b/src/libostree/ostree-repo-private.h
@@ -63,6 +63,7 @@ G_BEGIN_DECLS
/* Well-known keys for the additional metadata field in a commit in a ref entry
* in a summary file. */
#define OSTREE_COMMIT_TIMESTAMP "ostree.commit.timestamp"
+#define OSTREE_COMMIT_VERSION "ostree.commit.version"
typedef enum {
OSTREE_REPO_TEST_ERROR_PRE_COMMIT = (1 << 0),
diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c
index 65b567891..6ee8426d4 100644
--- a/src/libostree/ostree-repo-pull.c
+++ b/src/libostree/ostree-repo-pull.c
@@ -2244,6 +2244,9 @@ process_one_static_delta (OtPullData *pull_data,
ref, cancellable, error))
return FALSE;
+ if (!ostree_repo_mark_commit_partial (pull_data->repo, to_revision, TRUE, error))
+ return FALSE;
+
if (detached_data && !ostree_repo_write_commit_detached_metadata (pull_data->repo,
to_revision,
detached_data,
@@ -3706,13 +3709,13 @@ all_requested_refs_have_commit (GHashTable *requested_refs /* (element-type Ostr
* * `disable-verify-bindings` (`b`): Disable verification of commit bindings.
* Since: 2020.9
*/
-gboolean
-ostree_repo_pull_with_options (OstreeRepo *self,
- const char *remote_name_or_baseurl,
- GVariant *options,
- OstreeAsyncProgress *progress,
- GCancellable *cancellable,
- GError **error)
+static gboolean
+ostree_repo_pull_with_options_internal (OstreeRepo *self,
+ const char *remote_name_or_baseurl,
+ GVariant *options,
+ OstreeAsyncProgress *progress,
+ GCancellable *cancellable,
+ GError **error)
{
gboolean ret = FALSE;
g_autoptr(GBytes) bytes_summary = NULL;
@@ -5193,6 +5196,50 @@ ostree_repo_pull_with_options (OstreeRepo *self,
return ret;
}
+gboolean
+ostree_repo_pull_with_options (OstreeRepo *self,
+ const char *remote_name_or_baseurl,
+ GVariant *options,
+ OstreeAsyncProgress *progress,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GError) local_error = NULL;
+
+ if (!ostree_repo_pull_with_options_internal (self, remote_name_or_baseurl,
+ options, progress, cancellable,
+ &local_error))
+ {
+ gboolean disable_static_deltas = FALSE, require_static_deltas = FALSE;
+
+ if (options != NULL)
+ {
+ g_variant_lookup (options, "disable-static-deltas", "b", &disable_static_deltas);
+ g_variant_lookup (options, "require-static-deltas", "b", &require_static_deltas);
+ }
+
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
+ !disable_static_deltas && !require_static_deltas)
+ {
+ g_autoptr(GVariant) modified_options = NULL;
+ g_auto(GVariantDict) modified_options_dict;
+ g_variant_dict_init (&modified_options_dict, options);
+ g_variant_dict_insert (&modified_options_dict, "disable-static-deltas",
+ "b", TRUE);
+ modified_options = g_variant_dict_end (&modified_options_dict);
+
+ return ostree_repo_pull_with_options_internal (self, remote_name_or_baseurl,
+ modified_options, progress,
+ cancellable, error);
+ }
+
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/* Structure used in ostree_repo_find_remotes_async() which stores metadata
* about a given OSTree commit. This includes the metadata from the commit
* #GVariant, plus some working state which is used to work out which remotes
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index 86948ee26..891e113df 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -6064,10 +6064,11 @@ summary_add_ref_entry (OstreeRepo *self,
g_autoptr(GVariant) commit_obj = NULL;
if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, checksum, &commit_obj, error))
return FALSE;
+ g_autoptr(GVariant) orig_metadata = g_variant_get_child_value (commit_obj, 0);
g_variant_dict_init (&commit_metadata_builder, NULL);
- /* Forward the commit’s timestamp if it’s valid. */
+ /* Forward the commit’s timestamp and version if they're valid. */
guint64 commit_timestamp = ostree_commit_get_timestamp (commit_obj);
g_autoptr(GDateTime) dt = g_date_time_new_from_unix_utc (commit_timestamp);
@@ -6075,6 +6076,10 @@ summary_add_ref_entry (OstreeRepo *self,
g_variant_dict_insert_value (&commit_metadata_builder, OSTREE_COMMIT_TIMESTAMP,
g_variant_new_uint64 (GUINT64_TO_BE (commit_timestamp)));
+ const char *version = NULL;
+ if (g_variant_lookup (orig_metadata, OSTREE_COMMIT_META_KEY_VERSION, "&s", &version))
+ g_variant_dict_insert (&commit_metadata_builder, OSTREE_COMMIT_VERSION, "s", version);
+
g_variant_builder_add_value (refs_builder,
g_variant_new ("(s(t@ay@a{sv}))", ref,
(guint64) g_variant_get_size (commit_obj),
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
index c4ae86d54..e7f23cfdb 100644
--- a/src/libostree/ostree-sysroot-deploy.c
+++ b/src/libostree/ostree-sysroot-deploy.c
@@ -29,6 +29,8 @@
#include
#include
#include
+#include
+#include
#ifdef HAVE_LIBMOUNT
#include
@@ -54,6 +56,35 @@
#define OSTREE_DEPLOYMENT_FINALIZING_ID SD_ID128_MAKE(e8,64,6c,d6,3d,ff,46,25,b7,79,09,a8,e7,a4,09,94)
#endif
+static gboolean
+fs_is_fat (int parent_dfd)
+{
+ struct statfs buf;
+
+ if (TEMP_FAILURE_RETRY (fstatfs (parent_dfd, &buf)) < 0)
+ return FALSE;
+
+ if (buf.f_type == MSDOS_SUPER_MAGIC)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+symlink_fat (const char *target,
+ int parent_dfd,
+ const char *linkpath,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (!glnx_file_replace_contents_at(parent_dfd, linkpath, (guint8*)target,
+ -1, GLNX_FILE_REPLACE_DATASYNC_NEW,
+ cancellable, error))
+ return FALSE;
+
+ return TRUE;
+}
+
/*
* Like symlinkat() but overwrites (atomically) an existing
* symlink.
@@ -65,6 +96,9 @@ symlink_at_replace (const char *oldpath,
GCancellable *cancellable,
GError **error)
{
+ g_autofree char *dest_dir = g_path_get_dirname(newpath);
+ glnx_autofd int dest_dir_dfd = -1;
+ gboolean fat;
/* Possibly in the future generate a temporary random name here,
* would need to move "generate a temporary name" code into
* libglnx or glib?
@@ -74,12 +108,36 @@ symlink_at_replace (const char *oldpath,
/* Clean up any stale temporary links */
(void) unlinkat (parent_dfd, temppath, 0);
+ /* Clean up any stale FAT "symlinks" */
+ g_autofree char *temp_fat_path = g_strconcat (temppath, ".sln", NULL);
+ (void) unlinkat (parent_dfd, temp_fat_path, 0);
+
+ if (!glnx_opendirat (parent_dfd, dest_dir, TRUE, &dest_dir_dfd, error))
+ return FALSE;
+
+ fat = fs_is_fat (dest_dir_dfd);
+
/* Create the temp link */
- if (TEMP_FAILURE_RETRY (symlinkat (oldpath, parent_dfd, temppath)) < 0)
- return glnx_throw_errno_prefix (error, "symlinkat");
+ if (!fat)
+ {
+ if (TEMP_FAILURE_RETRY (symlinkat (oldpath, parent_dfd, temppath)) < 0)
+ return glnx_throw_errno_prefix (error, "symlinkat");
+ }
+ else
+ {
+ if (!symlink_fat (oldpath, parent_dfd, temp_fat_path, cancellable, error))
+ return glnx_throw_errno_prefix (error, "symlinkat");
+ }
/* Rename it into place */
- if (!glnx_renameat (parent_dfd, temppath, parent_dfd, newpath, error))
+ if (fat)
+ {
+ g_autofree char *new_fat_path = g_strconcat (newpath, ".sln", NULL);
+ if (!glnx_renameat (parent_dfd, temp_fat_path, parent_dfd, new_fat_path, error))
+ return FALSE;
+ return TRUE;
+ }
+ else if (!glnx_renameat (parent_dfd, temppath, parent_dfd, newpath, error))
return FALSE;
return TRUE;
@@ -991,6 +1049,8 @@ typedef struct {
char *initramfs_namever;
char *devicetree_srcpath;
char *devicetree_namever;
+ char *efi_blob_srcpath;
+ char *efi_blob_namever;
char *bootcsum;
} OstreeKernelLayout;
static void
@@ -1005,6 +1065,8 @@ _ostree_kernel_layout_free (OstreeKernelLayout *layout)
g_free (layout->initramfs_namever);
g_free (layout->devicetree_srcpath);
g_free (layout->devicetree_namever);
+ g_free (layout->efi_blob_srcpath);
+ g_free (layout->efi_blob_namever);
g_free (layout->bootcsum);
g_free (layout);
}
@@ -1168,6 +1230,15 @@ get_kernel_from_tree_usrlib_modules (OstreeSysroot *sysroot,
g_clear_object (&in);
glnx_close_fd (&fd);
+ if (!ot_openat_ignore_enoent (ret_layout->boot_dfd, "payg-image.efi", &fd, error))
+ return FALSE;
+ if (fd != -1)
+ {
+ ret_layout->efi_blob_srcpath = g_strdup ("payg-image.efi");
+ ret_layout->efi_blob_namever = g_strdup_printf ("payg-image-%s.efi", kver);
+ }
+ glnx_close_fd (&fd);
+
/* And finally, look for any HMAC file. This is needed for FIPS mode on some distros. */
if (!glnx_fstatat_allow_noent (ret_layout->boot_dfd, ".vmlinuz.hmac", NULL, 0, error))
return FALSE;
@@ -1731,6 +1802,26 @@ parse_os_release (const char *contents,
return ret;
}
+static gboolean
+is_payg_deployment(GCancellable *cancellable)
+{
+ g_autoptr(GFile) cmdline_file = g_file_new_for_path ("/proc/cmdline");
+ g_autofree char *cmdline = NULL;
+ gsize cmdline_len;
+
+ if (g_getenv ("OSTREE_DEPLOY_PAYG"))
+ return TRUE;
+
+ if (!g_file_load_contents (cmdline_file, cancellable,
+ &cmdline, &cmdline_len, NULL, NULL))
+ return FALSE;
+
+ if (g_strstr_len (cmdline, cmdline_len, "eospayg"))
+ return TRUE;
+
+ return FALSE;
+}
+
/* 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.
@@ -1746,6 +1837,8 @@ install_deployment_kernel (OstreeSysroot *sysroot,
GError **error)
{
+ gboolean payg;
+
GLNX_AUTO_PREFIX_ERROR ("Installing kernel", error);
OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment);
g_autofree char *deployment_dirpath = ostree_sysroot_get_deployment_dirpath (sysroot, deployment);
@@ -1785,24 +1878,46 @@ install_deployment_kernel (OstreeSysroot *sysroot,
if (!glnx_shutil_mkdir_p_at (sysroot->boot_fd, bootconfdir, 0775, cancellable, error))
return FALSE;
+ struct stat stbuf;
+ /* If this is a payg deployment, we want the efi blob and nothing else */
+ payg = kernel_layout->efi_blob_srcpath && is_payg_deployment(cancellable);
+ /* If we're updating an old loader entry that doesn't use efi blobs
+ * keep it the way it was. */
+ payg = payg && !ostree_bootconfig_parser_get (bootconfig, "linux");
+ if (payg)
+ {
+ g_assert (kernel_layout->efi_blob_namever);
+ if (!glnx_fstatat_allow_noent (bootcsum_dfd, kernel_layout->efi_blob_namever, &stbuf, 0, error))
+ return FALSE;
+ if (errno == ENOENT)
+ {
+ if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->efi_blob_srcpath,
+ bootcsum_dfd, kernel_layout->efi_blob_namever,
+ cancellable, error))
+ return FALSE;
+ }
+ }
+
/* Install (hardlink/copy) the kernel into /boot/ostree/osname-${bootcsum} if
* it doesn't exist already.
*/
- struct stat stbuf;
- if (!glnx_fstatat_allow_noent (bootcsum_dfd, kernel_layout->kernel_namever, &stbuf, 0, error))
- return FALSE;
- if (errno == ENOENT)
+ if (!payg)
{
- if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->kernel_srcpath,
- bootcsum_dfd, kernel_layout->kernel_namever,
- cancellable, error))
+ if (!glnx_fstatat_allow_noent (bootcsum_dfd, kernel_layout->kernel_namever, &stbuf, 0, error))
return FALSE;
+ if (errno == ENOENT)
+ {
+ if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->kernel_srcpath,
+ bootcsum_dfd, kernel_layout->kernel_namever,
+ cancellable, error))
+ return FALSE;
+ }
}
/* If we have an initramfs, then install it into
* /boot/ostree/osname-${bootcsum} if it doesn't exist already.
*/
- if (kernel_layout->initramfs_srcpath)
+ if (kernel_layout->initramfs_srcpath && !payg)
{
g_assert (kernel_layout->initramfs_namever);
if (!glnx_fstatat_allow_noent (bootcsum_dfd, kernel_layout->initramfs_namever, &stbuf, 0, error))
@@ -1816,7 +1931,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
}
}
- if (kernel_layout->devicetree_srcpath)
+ if (kernel_layout->devicetree_srcpath && !payg)
{
/* If devicetree_namever is set a single device tree is deployed */
if (kernel_layout->devicetree_namever)
@@ -1839,7 +1954,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
}
}
- if (kernel_layout->kernel_hmac_srcpath)
+ if (kernel_layout->kernel_hmac_srcpath && !payg)
{
if (!glnx_fstatat_allow_noent (bootcsum_dfd, kernel_layout->kernel_hmac_namever, &stbuf, 0, error))
return FALSE;
@@ -1853,7 +1968,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
}
g_autoptr(GPtrArray) overlay_initrds = NULL;
- for (char **it = _ostree_deployment_get_overlay_initrds (deployment); it && *it; it++)
+ for (char **it = _ostree_deployment_get_overlay_initrds (deployment); !payg && it && *it; it++)
{
char *checksum = *it;
@@ -1961,39 +2076,45 @@ install_deployment_kernel (OstreeSysroot *sysroot,
g_autofree char *version_key = g_strdup_printf ("%d", n_deployments - ostree_deployment_get_index (deployment));
ostree_bootconfig_parser_set (bootconfig, OSTREE_COMMIT_META_KEY_VERSION, version_key);
- g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->kernel_namever, NULL);
- ostree_bootconfig_parser_set (bootconfig, "linux", boot_relpath);
+
+ if (!payg)
+ {
+ g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->kernel_namever, NULL);
+ ostree_bootconfig_parser_set (bootconfig, "linux", boot_relpath);
+ }
val = ostree_bootconfig_parser_get (bootconfig, "options");
g_autoptr(OstreeKernelArgs) kargs = ostree_kernel_args_from_string (val);
- if (kernel_layout->initramfs_namever)
+ if (!payg)
{
- g_autofree char * initrd_boot_relpath =
- g_strconcat ("/", bootcsumdir, "/", kernel_layout->initramfs_namever, NULL);
- ostree_bootconfig_parser_set (bootconfig, "initrd", initrd_boot_relpath);
+ if (kernel_layout->initramfs_namever)
+ {
+ g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->initramfs_namever, NULL);
+ ostree_bootconfig_parser_set (bootconfig, "initrd", boot_relpath);
- if (overlay_initrds)
+ if (overlay_initrds)
+ {
+ g_ptr_array_add (overlay_initrds, NULL);
+ ostree_bootconfig_parser_set_overlay_initrds (bootconfig, (char**)overlay_initrds->pdata);
+ }
+ }
+ else
{
- g_ptr_array_add (overlay_initrds, NULL);
- ostree_bootconfig_parser_set_overlay_initrds (bootconfig, (char**)overlay_initrds->pdata);
+ g_autofree char *prepare_root_arg = NULL;
+ prepare_root_arg = g_strdup_printf ("init=/ostree/boot.%d/%s/%s/%d/usr/lib/ostree/ostree-prepare-root",
+ new_bootversion, osname, bootcsum,
+ ostree_deployment_get_bootserial (deployment));
+ ostree_kernel_args_replace_take (kargs, g_steal_pointer (&prepare_root_arg));
}
}
- else
- {
- g_autofree char *prepare_root_arg = NULL;
- prepare_root_arg = g_strdup_printf ("init=/ostree/boot.%d/%s/%s/%d/usr/lib/ostree/ostree-prepare-root",
- new_bootversion, osname, bootcsum,
- ostree_deployment_get_bootserial (deployment));
- ostree_kernel_args_replace_take (kargs, g_steal_pointer (&prepare_root_arg));
- }
- if (kernel_layout->devicetree_namever)
+ if (kernel_layout->devicetree_namever && !payg)
{
g_autofree char * dt_boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->devicetree_namever, NULL);
ostree_bootconfig_parser_set (bootconfig, "devicetree", dt_boot_relpath);
}
- else if (kernel_layout->devicetree_srcpath)
+ else if (kernel_layout->devicetree_srcpath && !payg)
{
/* If devicetree_srcpath is set but devicetree_namever is NULL, then we
* want to point to a whole directory of device trees.
@@ -2003,6 +2124,12 @@ install_deployment_kernel (OstreeSysroot *sysroot,
ostree_bootconfig_parser_set (bootconfig, "fdtdir", dt_boot_relpath);
}
+ if (payg)
+ {
+ g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->efi_blob_namever, NULL);
+ ostree_bootconfig_parser_set (bootconfig, "efi", boot_relpath);
+ }
+
/* Note this is parsed in ostree-impl-system-generator.c */
g_autofree char *ostree_kernel_arg = g_strdup_printf ("ostree=/ostree/boot.%d/%s/%s/%d",
new_bootversion, osname, bootcsum,
@@ -2039,11 +2166,18 @@ prepare_new_bootloader_link (OstreeSysroot *sysroot,
g_assert ((current_bootversion == 0 && new_bootversion == 1) ||
(current_bootversion == 1 && new_bootversion == 0));
+ /* Check if we're on a FAT FS, and unable to symlink */
+ gboolean fat = FALSE;
+ glnx_autofd int dest_dir_dfd = -1;
+ if (glnx_opendirat (sysroot->sysroot_fd, "boot", TRUE, &dest_dir_dfd, error))
+ fat = fs_is_fat (dest_dir_dfd);
+
/* This allows us to support both /boot on a seperate filesystem to / as well
* as on the same filesystem. */
- if (TEMP_FAILURE_RETRY (symlinkat (".", sysroot->sysroot_fd, "boot/boot")) < 0)
- if (errno != EEXIST)
- return glnx_throw_errno_prefix (error, "symlinkat");
+ if (!fat)
+ if (TEMP_FAILURE_RETRY (symlinkat (".", sysroot->sysroot_fd, "boot/boot")) < 0)
+ if (errno != EEXIST)
+ return glnx_throw_errno_prefix (error, "symlinkat");
g_autofree char *new_target = g_strdup_printf ("loader.%d", new_bootversion);
@@ -2073,12 +2207,22 @@ swap_bootloader (OstreeSysroot *sysroot,
if (!_ostree_sysroot_ensure_boot_fd (sysroot, error))
return FALSE;
+ gboolean fat = fs_is_fat (sysroot->boot_fd);
+
/* The symlink was already written, and we used syncfs() to ensure
* its data is in place. Renaming now should give us atomic semantics;
* see https://bugzilla.gnome.org/show_bug.cgi?id=755595
*/
- if (!glnx_renameat (sysroot->boot_fd, "loader.tmp", sysroot->boot_fd, "loader", error))
- return FALSE;
+ if (!fat)
+ {
+ if (!glnx_renameat (sysroot->boot_fd, "loader.tmp", sysroot->boot_fd, "loader", error))
+ return FALSE;
+ }
+ else
+ {
+ if (!glnx_renameat (sysroot->boot_fd, "loader.tmp.sln", sysroot->boot_fd, "loader.sln", error))
+ return FALSE;
+ }
/* Now we explicitly fsync this directory, even though it
* isn't required for atomicity, for two reasons:
diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c
index eccf9375a..34b9e1680 100644
--- a/src/libostree/ostree-sysroot.c
+++ b/src/libostree/ostree-sysroot.c
@@ -621,33 +621,38 @@ read_current_bootversion (OstreeSysroot *self,
GCancellable *cancellable,
GError **error)
{
- int ret_bootversion;
+ int ret_bootversion = 0;
struct stat stbuf;
+ g_autofree char *target = NULL;
if (!glnx_fstatat_allow_noent (self->sysroot_fd, "boot/loader", &stbuf, AT_SYMLINK_NOFOLLOW, error))
return FALSE;
if (errno == ENOENT)
{
- g_debug ("Didn't find $sysroot/boot/loader symlink; assuming bootversion 0");
- ret_bootversion = 0;
+ /* Don't share the error because we want error handling to be the same as before we
+ * added fake symlinks */
+ g_debug ("Didn't find $sysroot/boot/loader symlink; trying fake symlink");
+ target = glnx_file_get_contents_utf8_at(self->sysroot_fd, "boot/loader.sln", NULL, cancellable, NULL);
+ if (!target) goto not_found;
}
else
{
if (!S_ISLNK (stbuf.st_mode))
return glnx_throw (error, "Not a symbolic link: boot/loader");
-
- g_autofree char *target =
+ target =
glnx_readlinkat_malloc (self->sysroot_fd, "boot/loader", cancellable, error);
if (!target)
return FALSE;
- if (g_strcmp0 (target, "loader.0") == 0)
- ret_bootversion = 0;
- else if (g_strcmp0 (target, "loader.1") == 0)
- ret_bootversion = 1;
- else
- return glnx_throw (error, "Invalid target '%s' in boot/loader", target);
}
+ if (g_strcmp0 (target, "loader.0") == 0)
+ ret_bootversion = 0;
+ else if (g_strcmp0 (target, "loader.1") == 0)
+ ret_bootversion = 1;
+ else
+ return glnx_throw (error, "Invalid target '%s' in boot/loader", target);
+
+not_found:
*out_bootversion = ret_bootversion;
return TRUE;
}
diff --git a/src/libotutil/ot-variant-builder.c b/src/libotutil/ot-variant-builder.c
index 92ac8edea..b7a7ec759 100644
--- a/src/libotutil/ot-variant-builder.c
+++ b/src/libotutil/ot-variant-builder.c
@@ -830,7 +830,7 @@ static void
ot_variant_builder_info_free (OtVariantBuilderInfo *info)
{
if (info->parent)
- ot_variant_builder_info_free (info);
+ ot_variant_builder_info_free (info->parent);
g_variant_type_free (info->type);
g_array_unref (info->child_ends);
diff --git a/src/ostree/ot-admin-builtin-finalize-staged.c b/src/ostree/ot-admin-builtin-finalize-staged.c
index 80d8a9b79..ac08c21be 100644
--- a/src/ostree/ot-admin-builtin-finalize-staged.c
+++ b/src/ostree/ot-admin-builtin-finalize-staged.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2018 Red Hat, Inc.
+ * Copyright (C) 2022 Endless OS Foundation LLC
*
* SPDX-License-Identifier: LGPL-2.0+
*
@@ -19,8 +20,9 @@
#include "config.h"
-#include "config.h"
-
+#include
+#include
+#include
#include
#include "ot-main.h"
@@ -32,7 +34,10 @@
#include "ostree-cmdprivate.h"
#include "ostree.h"
+static gboolean opt_hold;
+
static GOptionEntry options[] = {
+ { "hold", 0, 0, G_OPTION_ARG_NONE, &opt_hold, "Hold /boot open during finalization", NULL },
{ NULL }
};
@@ -50,13 +55,52 @@ ot_admin_builtin_finalize_staged (int argc, char **argv, OstreeCommandInvocation
g_autoptr(GOptionContext) context = g_option_context_new ("");
g_autoptr(OstreeSysroot) sysroot = NULL;
+
+ /* First parse the args without loading the sysroot to see what options are
+ * set. */
if (!ostree_admin_option_context_parse (context, options, &argc, &argv,
- OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER,
+ OSTREE_ADMIN_BUILTIN_FLAG_NO_LOAD,
invocation, &sysroot, cancellable, error))
return FALSE;
- if (!ostree_cmd__private__()->ostree_finalize_staged (sysroot, cancellable, error))
- return FALSE;
+ if (opt_hold)
+ {
+ /* Load the sysroot unlocked so that a separate namespace isn't
+ * created. */
+ if (!ostree_admin_sysroot_load (sysroot,
+ OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER | OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED,
+ cancellable, error))
+ return FALSE;
+
+ /* In case it's an automount, open /boot so that the automount doesn't
+ * expire until before this process exits. If it did expire and got
+ * unmounted, the service would be stopped and the deployment would be
+ * finalized earlier than expected.
+ */
+ int sysroot_fd = ostree_sysroot_get_fd (sysroot);
+ glnx_autofd int boot_fd = -1;
+ g_debug ("Opening /boot directory");
+ if (!glnx_opendirat (sysroot_fd, "boot", TRUE, &boot_fd, error))
+ return FALSE;
+
+ /* We want to keep /boot open until the deployment is finalized during
+ * system shutdown, so block until we get SIGTERM which systemd will send
+ * when the unit is stopped.
+ */
+ pause ();
+ }
+ else
+ {
+ /* Load the sysroot with the normal flags and actually finalize the
+ * deployment. */
+ if (!ostree_admin_sysroot_load (sysroot,
+ OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER,
+ cancellable, error))
+ return FALSE;
+
+ if (!ostree_cmd__private__()->ostree_finalize_staged (sysroot, cancellable, error))
+ return FALSE;
+ }
return TRUE;
}
diff --git a/src/ostree/ot-dump.c b/src/ostree/ot-dump.c
index b874db0fe..509eb792d 100644
--- a/src/ostree/ot-dump.c
+++ b/src/ostree/ot-dump.c
@@ -249,6 +249,11 @@ dump_summary_ref (const char *collection_id,
pretty_key = "Timestamp";
value_str = uint64_secs_to_iso8601 (GUINT64_FROM_BE (g_variant_get_uint64 (value)));
}
+ else if (g_strcmp0 (key, OSTREE_COMMIT_VERSION) == 0)
+ {
+ pretty_key = "Version";
+ value_str = g_strdup (g_variant_get_string (value, NULL));
+ }
else
{
value_str = g_variant_print (value, FALSE);
diff --git a/src/ostree/ot-main.c b/src/ostree/ot-main.c
index 8ee730382..9ad357666 100644
--- a/src/ostree/ot-main.c
+++ b/src/ostree/ot-main.c
@@ -564,41 +564,11 @@ on_sysroot_journal_msg (OstreeSysroot *sysroot,
}
gboolean
-ostree_admin_option_context_parse (GOptionContext *context,
- const GOptionEntry *main_entries,
- int *argc,
- char ***argv,
- OstreeAdminBuiltinFlags flags,
- OstreeCommandInvocation *invocation,
- OstreeSysroot **out_sysroot,
- GCancellable *cancellable,
- GError **error)
+ostree_admin_sysroot_load (OstreeSysroot *sysroot,
+ OstreeAdminBuiltinFlags flags,
+ GCancellable *cancellable,
+ GError **error)
{
- /* Entries are listed in --help output in the order added. We add the
- * main entries ourselves so that we can add the --sysroot entry first. */
-
- g_option_context_add_main_entries (context, global_admin_entries, NULL);
-
- if (!ostree_option_context_parse (context, main_entries, argc, argv,
- invocation, NULL, cancellable, error))
- return FALSE;
-
- if (!opt_print_current_dir && (flags & OSTREE_ADMIN_BUILTIN_FLAG_NO_SYSROOT))
- {
- g_assert_null (out_sysroot);
- /* Early return if no sysroot is requested */
- return TRUE;
- }
-
- g_autoptr(GFile) sysroot_path = NULL;
- if (opt_sysroot != NULL)
- sysroot_path = g_file_new_for_path (opt_sysroot);
-
- g_autoptr(OstreeSysroot) sysroot = ostree_sysroot_new (sysroot_path);
- if (!ostree_sysroot_initialize (sysroot, error))
- return FALSE;
- g_signal_connect (sysroot, "journal-msg", G_CALLBACK (on_sysroot_journal_msg), NULL);
-
if ((flags & OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED) == 0)
{
/* If we're requested to lock the sysroot, first check if we're operating
@@ -642,6 +612,51 @@ ostree_admin_option_context_parse (GOptionContext *context,
}
}
+ return TRUE;
+}
+
+gboolean
+ostree_admin_option_context_parse (GOptionContext *context,
+ const GOptionEntry *main_entries,
+ int *argc,
+ char ***argv,
+ OstreeAdminBuiltinFlags flags,
+ OstreeCommandInvocation *invocation,
+ OstreeSysroot **out_sysroot,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* Entries are listed in --help output in the order added. We add the
+ * main entries ourselves so that we can add the --sysroot entry first. */
+
+ g_option_context_add_main_entries (context, global_admin_entries, NULL);
+
+ if (!ostree_option_context_parse (context, main_entries, argc, argv,
+ invocation, NULL, cancellable, error))
+ return FALSE;
+
+ if (!opt_print_current_dir && (flags & OSTREE_ADMIN_BUILTIN_FLAG_NO_SYSROOT))
+ {
+ g_assert_null (out_sysroot);
+ /* Early return if no sysroot is requested */
+ return TRUE;
+ }
+
+ g_autoptr(GFile) sysroot_path = NULL;
+ if (opt_sysroot != NULL)
+ sysroot_path = g_file_new_for_path (opt_sysroot);
+
+ g_autoptr(OstreeSysroot) sysroot = ostree_sysroot_new (sysroot_path);
+ if (!ostree_sysroot_initialize (sysroot, error))
+ return FALSE;
+ g_signal_connect (sysroot, "journal-msg", G_CALLBACK (on_sysroot_journal_msg), NULL);
+
+ if (opt_print_current_dir || (flags & OSTREE_ADMIN_BUILTIN_FLAG_NO_LOAD) == 0)
+ {
+ if (!ostree_admin_sysroot_load (sysroot, flags, cancellable, error))
+ return FALSE;
+ }
+
if (opt_print_current_dir)
{
g_autoptr(GPtrArray) deployments = NULL;
diff --git a/src/ostree/ot-main.h b/src/ostree/ot-main.h
index b369deb87..e296501ae 100644
--- a/src/ostree/ot-main.h
+++ b/src/ostree/ot-main.h
@@ -36,6 +36,7 @@ typedef enum {
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER = (1 << 0),
OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED = (1 << 1),
OSTREE_ADMIN_BUILTIN_FLAG_NO_SYSROOT = (1 << 2),
+ OSTREE_ADMIN_BUILTIN_FLAG_NO_LOAD = (1 << 3),
} OstreeAdminBuiltinFlags;
@@ -91,6 +92,11 @@ gboolean ostree_admin_option_context_parse (GOptionContext *context,
OstreeSysroot **out_sysroot,
GCancellable *cancellable, GError **error);
+gboolean ostree_admin_sysroot_load (OstreeSysroot *sysroot,
+ OstreeAdminBuiltinFlags flags,
+ GCancellable *cancellable,
+ GError **error);
+
gboolean ostree_ensure_repo_writable (OstreeRepo *repo, GError **error);
void ostree_print_gpg_verify_result (OstreeGpgVerifyResult *result);
diff --git a/tests/inst/src/destructive.rs b/tests/inst/src/destructive.rs
index 2e2bd374b..41fc53326 100644
--- a/tests/inst/src/destructive.rs
+++ b/tests/inst/src/destructive.rs
@@ -463,6 +463,7 @@ fn impl_transaction_test>(
"
systemctl stop rpm-ostreed
systemctl stop ostree-finalize-staged
+ systemctl stop ostree-finalize-staged-hold
ostree reset testrepo:{testref} {booted_commit}
rpm-ostree cleanup -pbrm
",
@@ -504,7 +505,8 @@ fn impl_transaction_test>(
InterruptStrategy::Force(ForceInterruptStrategy::Kill9) => {
bash!(
"systemctl kill -s KILL rpm-ostreed || true
- systemctl kill -s KILL ostree-finalize-staged || true"
+ systemctl kill -s KILL ostree-finalize-staged || true
+ systemctl kill -s KILL ostree-finalize-staged-hold || true"
)?;
live_strategy = Some(strategy);
}
@@ -528,7 +530,8 @@ fn impl_transaction_test>(
InterruptStrategy::Polite(PoliteInterruptStrategy::Stop) => {
bash!(
"systemctl stop rpm-ostreed || true
- systemctl stop ostree-finalize-staged || true"
+ systemctl stop ostree-finalize-staged || true
+ systemctl stop ostree-finalize-staged-hold || true"
)?;
live_strategy = Some(strategy);
}
diff --git a/tests/kolainst/destructive/boot-automount.sh b/tests/kolainst/destructive/boot-automount.sh
new file mode 100755
index 000000000..d6d1732e7
--- /dev/null
+++ b/tests/kolainst/destructive/boot-automount.sh
@@ -0,0 +1,83 @@
+#!/bin/bash
+# https://github.com/ostreedev/ostree/issues/2543
+set -xeuo pipefail
+
+. ${KOLA_EXT_DATA}/libinsttest.sh
+
+case "${AUTOPKGTEST_REBOOT_MARK:-}" in
+ "")
+ # Ensure boot is mount point
+ mountpoint /boot
+
+ # Create an automount unit with an extremely short timeout
+ cat > /etc/systemd/system/boot.automount <<"EOF"
+[Automount]
+Where=/boot
+TimeoutIdleSec=1
+
+[Install]
+WantedBy=local-fs.target
+EOF
+ systemctl daemon-reload
+ systemctl enable boot.automount
+
+ # Unmount /boot, start the automount unit, and ensure the units are
+ # in the correct states.
+ umount /boot
+ systemctl start boot.automount
+ boot_state=$(systemctl show -P ActiveState boot.mount)
+ boot_auto_state=$(systemctl show -P ActiveState boot.automount)
+ assert_streq "${boot_state}" inactive
+ assert_streq "${boot_auto_state}" active
+
+ # Trigger a new staged deployment and check that the relevant units
+ # are enabled.
+ ostree admin deploy --stage --karg-append=somedummykarg=1 "${host_commit}"
+ rpm-ostree status --json
+ deployment_staged=$(rpmostree_query_json '.deployments[0].staged')
+ assert_streq "${deployment_staged}" true
+ test -f /run/ostree/staged-deployment
+ finalize_staged_state=$(systemctl show -P ActiveState ostree-finalize-staged.service)
+ finalize_staged_hold_state=$(systemctl show -P ActiveState ostree-finalize-staged-hold.service)
+ assert_streq "${finalize_staged_state}" active
+ assert_streq "${finalize_staged_hold_state}" active
+
+ # Sleep 1 second to ensure the boot automount idle timeout has
+ # passed and then check that /boot is still mounted.
+ sleep 1
+ boot_state=$(systemctl show -P ActiveState boot.mount)
+ assert_streq "${boot_state}" active
+
+ /tmp/autopkgtest-reboot "2"
+ ;;
+ "2")
+ rpm-ostree status --json
+ deployment_staged=$(rpmostree_query_json '.deployments[0].staged')
+ assert_streq "${deployment_staged}" false
+ test ! -f /run/ostree/staged-deployment
+ assert_file_has_content_literal /proc/cmdline somedummykarg=1
+
+ # Check that the finalize and hold services succeeded in the
+ # previous boot. Dump them to the test log to help debugging.
+ prepare_tmpdir
+ journalctl -b -1 -o short-monotonic \
+ -u ostree-finalize-staged.service \
+ -u ostree-finalize-staged-hold.service \
+ -u boot.mount \
+ -u boot.automount \
+ > logs.txt
+ cat logs.txt
+ assert_file_has_content logs.txt 'ostree-finalize-staged.service: \(Succeeded\|Deactivated successfully\)'
+ assert_file_has_content logs.txt 'ostree-finalize-staged-hold.service: \(Succeeded\|Deactivated successfully\)'
+
+ # Check that the hold service remained active and kept /boot mounted until
+ # the finalize service completed.
+ finalize_stopped=$(journalctl -b -1 -o json -g Stopped -u ostree-finalize-staged.service | tail -n1 | jq -r .__MONOTONIC_TIMESTAMP)
+ hold_stopping=$(journalctl -b -1 -o json -g Stopping -u ostree-finalize-staged-hold.service | tail -n1 | jq -r .__MONOTONIC_TIMESTAMP)
+ hold_stopped=$(journalctl -b -1 -o json -g Stopped -u ostree-finalize-staged-hold.service | tail -n1 | jq -r .__MONOTONIC_TIMESTAMP)
+ boot_unmounting=$(journalctl -b -1 -o json -g Unmounting -u boot.mount | tail -n1 | jq -r .__MONOTONIC_TIMESTAMP)
+ test "${finalize_stopped}" -lt "${hold_stopping}"
+ test "${hold_stopped}" -lt "${boot_unmounting}"
+ ;;
+ *) fatal "Unexpected AUTOPKGTEST_REBOOT_MARK=${AUTOPKGTEST_REBOOT_MARK}" ;;
+esac
diff --git a/tests/test-pull-repeated.sh b/tests/test-pull-repeated.sh
index 4c321618b..3b7d10b18 100755
--- a/tests/test-pull-repeated.sh
+++ b/tests/test-pull-repeated.sh
@@ -21,6 +21,10 @@ set -euo pipefail
. $(dirname $0)/libtest.sh
+if [ "${OSTREE_TEST_ALLOW_RANDOM:-}" != 1 ]; then
+ skip "Non-deterministic test will fail if we are unlucky"
+fi
+
COMMIT_SIGN=""
if has_gpgme; then
COMMIT_SIGN="--gpg-homedir=${TEST_GPG_KEYHOME} --gpg-sign=${TEST_GPG_KEYID_1}"
diff --git a/tests/test-summary-view.sh b/tests/test-summary-view.sh
index 088628d8a..9dfc74f44 100755
--- a/tests/test-summary-view.sh
+++ b/tests/test-summary-view.sh
@@ -56,6 +56,7 @@ assert_file_has_content_literal summary.txt "* main"
assert_file_has_content_literal summary.txt "* other"
assert_file_has_content_literal summary.txt "ostree.summary.last-modified"
assert_file_has_content_literal summary.txt "Timestamp (ostree.commit.timestamp): "
+assert_file_has_content_literal summary.txt "Version (ostree.commit.version): 3.2"
echo "ok view summary"
# Check the summary can be viewed raw too.