diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py index 0c4f4c6ad9..36cfa74b54 100644 --- a/dnf/cli/cli.py +++ b/dnf/cli/cli.py @@ -214,6 +214,15 @@ def do_transaction(self, display=()): elif 'test' in self.conf.tsflags: logger.info(_("{prog} will only download packages, install gpg keys, and check the " "transaction.").format(prog=dnf.util.MAIN_PROG_UPPER)) + if dnf.util._is_bootc_host() and \ + os.path.realpath(self.conf.installroot) == "/": + _bootc_host_msg = _(""" +*** Error: system is configured to be read-only; for more +*** information run `bootc --help`. +""") + logger.info(_bootc_host_msg) + raise CliError(_("Operation aborted.")) + if self._promptWanted(): if self.conf.assumeno or not self.output.userconfirm(): raise CliError(_("Operation aborted.")) diff --git a/dnf/util.py b/dnf/util.py index 16c5bc89c1..e8f587a030 100644 --- a/dnf/util.py +++ b/dnf/util.py @@ -631,3 +631,17 @@ def _tsi_or_pkg_nevra_cmp(item1, item2): def _name_unset_wrapper(input_name): # returns for everything that evaluates to False (None, empty..) return input_name if input_name else _("") + + +def _is_bootc_host(): + """Returns true is the system is managed as an immutable container, + false otherwise. If msg is True, a warning message is displayed + for the user. + """ + ostree_booted = '/run/ostree-booted' + usr = '/usr/' + # Check if usr is writtable and we are in a running ostree system. + # We want this code to return true only when the system is in locked state. If someone ran + # bootc overlay or ostree admin unlock we would want normal DNF path to be ran as it will be + # temporary changes (until reboot). + return os.path.isfile(ostree_booted) and not os.access(usr, os.W_OK)