Revert "sysroot: Merge bootconfig-extra from previously staged deployment"
authorColin Walters <walters@verbum.org>
Tue, 7 Jul 2026 14:59:35 +0000 (10:59 -0400)
committerColin Walters <walters@verbum.org>
Tue, 7 Jul 2026 15:16:27 +0000 (11:16 -0400)
This reverts commit 4c0d3a819033166ec9753ff8c5fdac5db1bac813.

The 3-way merge of bootconfig-extra during staging breaks bootc
loader-entries set-options-for-source when called multiple times on the
same boot.  Each call sets the desired x-options-source-* keys on the
merge deployment's in-memory bootconfig, but the merge logic gives
higher priority to the previously staged data, overriding the caller's
updates.  This corrupts the source-tracking metadata that bootc relies
on to compute override_kernel_argv, causing stale kargs to persist
across reboots.

The scenario this commit was trying to fix (rpm-ostree re-staging after
bootc on the same boot, preserving bootc's extension keys) will need a
different approach that does not conflict with callers that update
extension keys across multiple stagings.

Signed-off-by: Colin Walters <walters@verbum.org>
src/libostree/ostree-sysroot-deploy.c

index add835fb9b04a3cd073c68bab0a445bfe349fdc7..77b601ff42275da3750c7a7f46b785fc5c1630d4 100644 (file)
@@ -3795,25 +3795,14 @@ _ostree_sysroot_ensure_finalize_staged_service (GError **error)
   return TRUE;
 }
 
-/* Merge all entries from an a{ss} GVariant into a string→string hash table.
- * Used to accumulate bootconfig-extra keys from multiple sources during staging. */
-static void
-merge_extra_variant_into_table (GVariant *extra, GHashTable *table)
-{
-  GVariantIter iter;
-  const char *k, *v;
-  g_variant_iter_init (&iter, extra);
-  while (g_variant_iter_next (&iter, "{&s&s}", &k, &v))
-    g_hash_table_insert (table, g_strdup (k), g_strdup (v));
-}
-
 /**
  * ostree_sysroot_stage_tree_with_options:
  * @self: Sysroot
- * @osname: osname to use for merge deployment
+ * @osname: (allow-none): osname to use for merge deployment
  * @revision: Checksum to add
  * @origin: (allow-none): Origin to use for upgrades
- * @opts: (nullable): Options
+ * @merge_deployment: (allow-none): Use this deployment for merge path
+ * @opts: Options
  * @out_new_deployment: (out): The new deployment path
  * @cancellable: Cancellable
  * @error: Error
@@ -3899,72 +3888,27 @@ ostree_sysroot_stage_tree_with_options (OstreeSysroot *self, const char *osname,
    * These are custom keys set by consumers like bootc and need to survive
    * the staging roundtrip so they are preserved during finalization at shutdown.
    *
-   * Extension keys can come from three sources, merged in increasing
-   * priority order (higher-priority sources override lower ones for
-   * the same key):
-   *
-   *   1. The merge deployment's bootconfig (on-disk BLS from the
-   *      currently booted or pending deployment) — lowest priority
-   *   2. The previously staged deployment's bootconfig-extra (a prior
-   *      consumer like bootc may have staged keys that would be lost
-   *      when this new staging replaces the old staged GVariant)
-   *   3. The new deployment's bootconfig (caller set keys directly)
-   *      — highest priority
-   *
-   * This merge ensures that e.g. `bootc loader-entries set-options-for-source`
-   * followed by `rpm-ostree kargs --append` on the same boot preserves the
-   * source keys that bootc wrote into the first staged deployment.
+   * First check the new deployment's bootconfig (in case the caller set keys
+   * on it directly).  If none found, fall back to the merge deployment's
+   * bootconfig, which carries the keys from the currently deployed BLS entry.
+   * This ensures that x-prefixed keys are inherited across staged deployments
+   * even though _ostree_deployment_set_bootconfig_from_kargs() creates a fresh
+   * bootconfig containing only the "options" key.
    */
   {
-    g_autoptr (GHashTable) merged_extra
-        = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-
-    /* Priority 1 (lowest): merge deployment's bootconfig */
-    if (merge_deployment)
+    GVariant *extra = NULL;
+    OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment);
+    if (bootconfig)
+      extra = _ostree_bootconfig_parser_get_extra_keys_variant (bootconfig);
+    if (!extra && merge_deployment)
       {
         OstreeBootconfigParser *merge_bootconfig
             = ostree_deployment_get_bootconfig (merge_deployment);
         if (merge_bootconfig)
-          {
-            g_autoptr (GVariant) merge_extra
-                = _ostree_bootconfig_parser_get_extra_keys_variant (merge_bootconfig);
-            if (merge_extra)
-              merge_extra_variant_into_table (merge_extra, merged_extra);
-          }
-      }
-
-    /* Priority 2: previously staged deployment's bootconfig-extra.
-     * The staged deployment data is already loaded and cached in the
-     * OstreeSysroot during ostree_sysroot_load(). */
-    if (self->staged_deployment_data)
-      {
-        g_autoptr (GVariant) prev_extra = g_variant_lookup_value (
-            self->staged_deployment_data, "bootconfig-extra", (GVariantType *)"a{ss}");
-        if (prev_extra)
-          merge_extra_variant_into_table (prev_extra, merged_extra);
-      }
-
-    /* Priority 3 (highest): new deployment's bootconfig */
-    {
-      OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment);
-      if (bootconfig)
-        {
-          g_autoptr (GVariant) new_extra
-              = _ostree_bootconfig_parser_get_extra_keys_variant (bootconfig);
-          if (new_extra)
-            merge_extra_variant_into_table (new_extra, merged_extra);
-        }
-    }
-
-    if (g_hash_table_size (merged_extra) > 0)
-      {
-        g_auto (GVariantBuilder) extra_builder = OT_VARIANT_BUILDER_INITIALIZER;
-        g_variant_builder_init (&extra_builder, (GVariantType *)"a{ss}");
-        GLNX_HASH_TABLE_FOREACH_KV (merged_extra, const char *, k, const char *, v)
-          g_variant_builder_add (&extra_builder, "{ss}", k, v);
-        g_variant_builder_add (builder, "{sv}", "bootconfig-extra",
-                               g_variant_builder_end (&extra_builder));
+          extra = _ostree_bootconfig_parser_get_extra_keys_variant (merge_bootconfig);
       }
+    if (extra)
+      g_variant_builder_add (builder, "{sv}", "bootconfig-extra", extra);
   }
 
   const char *parent = dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED));