From b03f2882d397453be192b79516c7dad517f36e7d Mon Sep 17 00:00:00 2001 From: Evan Goode Date: Wed, 25 Mar 2026 15:52:16 -0400 Subject: [PATCH] bootc: unlock only if /usr is read-only Resolves: RHEL-138512 --- ...ootc-unlock-only-if-usr-is-read-only.patch | 57 +++++++++++++++++++ ...writable-when-DeploymentUnlockedStat.patch | 37 ++++++++++++ dnf.spec | 7 ++- 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 0070-bootc-unlock-only-if-usr-is-read-only.patch create mode 100644 0071-bootc-Call-make_writable-when-DeploymentUnlockedStat.patch diff --git a/0070-bootc-unlock-only-if-usr-is-read-only.patch b/0070-bootc-unlock-only-if-usr-is-read-only.patch new file mode 100644 index 0000000..55a562f --- /dev/null +++ b/0070-bootc-unlock-only-if-usr-is-read-only.patch @@ -0,0 +1,57 @@ +From a8608927848b918e82d4b78bc0312cfdb58210de Mon Sep 17 00:00:00 2001 +From: Evan Goode +Date: Mon, 1 Dec 2025 13:40:26 -0500 +Subject: [PATCH 1/2] bootc: unlock only if /usr is read-only + +DNF should only run `ostree admin unlock --transient` if `/usr` is +actually read-only. `/usr` may be writable via OSTree's `root.transient = +true` even if the `ostree admin status` is not transient. + +Resolves: https://redhat.atlassian.net/browse/RHEL-138512 +--- + dnf/cli/cli.py | 4 +++- + dnf/util.py | 2 +- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py +index 21e8764d0..f855c5eec 100644 +--- a/dnf/cli/cli.py ++++ b/dnf/cli/cli.py +@@ -225,6 +225,7 @@ class BaseCli(dnf.Base): + # Handle bootc transactions. `--transient` must be specified if + # /usr is not already writeable. + bootc_system = None ++ bootc_system_needs_unlock = False + if is_bootc_transaction: + if self.conf.persistence == "persist": + logger.info(_("Persistent transactions aren't supported on bootc systems.")) +@@ -246,6 +247,7 @@ class BaseCli(dnf.Base): + logger.info(_("A transient overlay will be created on /usr that will be discarded on reboot. " + "Keep in mind that changes to /etc and /var will still persist, and packages " + "commonly modify these directories.")) ++ bootc_system_needs_unlock = True + self._persistence = libdnf.transaction.TransactionPersistence_TRANSIENT + + # Check whether the transaction modifies usr_drift_protected_paths +@@ -276,7 +278,7 @@ class BaseCli(dnf.Base): + if self.conf.assumeno or not self.output.userconfirm(): + raise CliError(_("Operation aborted.")) + +- if bootc_system: ++ if bootc_system and bootc_system_needs_unlock: + bootc_system.make_writable() + else: + logger.info(_('Nothing to do.')) +diff --git a/dnf/util.py b/dnf/util.py +index 0161f80d8..70bae605f 100644 +--- a/dnf/util.py ++++ b/dnf/util.py +@@ -741,4 +741,4 @@ class _BootcSystem: + # read-only. Set up a mount namespace for DNF. + self._set_up_mountns() + +- assert os.access(self.usr, os.W_OK) ++ assert self.is_writable() +-- +2.53.0 + diff --git a/0071-bootc-Call-make_writable-when-DeploymentUnlockedStat.patch b/0071-bootc-Call-make_writable-when-DeploymentUnlockedStat.patch new file mode 100644 index 0000000..fd9b520 --- /dev/null +++ b/0071-bootc-Call-make_writable-when-DeploymentUnlockedStat.patch @@ -0,0 +1,37 @@ +From b9c0a2ab768c624ad7746a175379358479df6f55 Mon Sep 17 00:00:00 2001 +From: Evan Goode +Date: Fri, 20 Mar 2026 16:28:14 -0400 +Subject: [PATCH 2/2] bootc: Call make_writable when + DeploymentUnlockedState.TRANSIENT + +Fixes a bug in 1afe4328334f27b45b5c4599b6f1e8ac69d465e4. +bootc_system.make_writable should still be called even when the system +is already in DeploymentUnlockedState.TRANSIENT, since the DNF mount +namespace needs to be set up either way. +--- + dnf/cli/cli.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py +index f855c5eec..ce81f262b 100644 +--- a/dnf/cli/cli.py ++++ b/dnf/cli/cli.py +@@ -235,6 +235,7 @@ class BaseCli(dnf.Base): + bootc_system = dnf.util._BootcSystem() + + if not bootc_system.is_writable(): ++ bootc_system_needs_unlock = True + if self.conf.persistence == "auto": + logger.info(_("This bootc system is configured to be read-only. Pass --transient to " + "perform this transaction in a transient overlay which will reset when " +@@ -247,7 +248,6 @@ class BaseCli(dnf.Base): + logger.info(_("A transient overlay will be created on /usr that will be discarded on reboot. " + "Keep in mind that changes to /etc and /var will still persist, and packages " + "commonly modify these directories.")) +- bootc_system_needs_unlock = True + self._persistence = libdnf.transaction.TransactionPersistence_TRANSIENT + + # Check whether the transaction modifies usr_drift_protected_paths +-- +2.53.0 + diff --git a/dnf.spec b/dnf.spec index 1ff455a..0394412 100644 --- a/dnf.spec +++ b/dnf.spec @@ -73,7 +73,7 @@ It supports RPMs, modules and comps groups & environments. Name: dnf Version: 4.14.0 -Release: 33%{?dist} +Release: 34%{?dist} Summary: %{pkg_summary} # For a breakdown of the licensing, see PACKAGE-LICENSING License: GPLv2+ @@ -148,6 +148,8 @@ Patch66: 0066-Document-how-releasever-releasever_-major-minor-affe.patch Patch67: 0067-Move-releasever_minor-setter-docstring-to-the-correc.patch Patch68: 0068-automatic-Expand-email_to-in-command_email-emitter-t.patch Patch69: 0069-autoremove-warn-and-skip-dangling-protected-dependen.patch +Patch70: 0070-bootc-unlock-only-if-usr-is-read-only.patch +Patch71: 0071-bootc-Call-make_writable-when-DeploymentUnlockedStat.patch BuildArch: noarch BuildRequires: cmake @@ -454,6 +456,9 @@ popd # bootc subpackage does not include any files %changelog +* Wed Mar 25 2026 Evan Goode - 4.14.0-34 +- bootc: unlock only if /usr is read-only (RHEL-138512) + * Wed Feb 11 2026 Ales Matej - 4.14.0-33 - autoremove: when a dangling protected dependency is found produce a wanrning and skip it (RHEL-76112)