b45ed7
From 13acd1e7439fe6e587b0f01843755a493a83c88e Mon Sep 17 00:00:00 2001
b45ed7
From: Colin Walters <walters@verbum.org>
b45ed7
Date: Thu, 27 Jul 2017 10:43:24 -0400
b45ed7
Subject: [PATCH] rpmostreepayload: Rollup backport of Fedora patches
b45ed7
b45ed7
This rolls up all of <https://github.com/rhinstaller/anaconda/pull/1129> which
b45ed7
is basically mount handling, plus the pull-local optimization for kickstarts.
b45ed7
b45ed7
We need to change the implementation for the mount setup, since
b45ed7
things work differently in RHEL7 vs Fedora since commit
b45ed7
664ef7b43f9102aa9332d0db5b7d13f8ece436f0.
b45ed7
b45ed7
The code that changed in `pyanaconda/install.py` only applied to ostree-based
b45ed7
payloads (where we introduced the physical vs sysroot distinction), so should be
b45ed7
safe for backporting.
b45ed7
b45ed7
Resolves: rhbz#1459623
b45ed7
---
b45ed7
 pyanaconda/install.py                    |  17 +---
b45ed7
 pyanaconda/packaging/rpmostreepayload.py | 129 ++++++++++++++++++++++++++-----
b45ed7
 2 files changed, 111 insertions(+), 35 deletions(-)
b45ed7
b45ed7
diff --git a/pyanaconda/install.py b/pyanaconda/install.py
b45ed7
index 26e1b2680..a711813af 100644
b45ed7
--- a/pyanaconda/install.py
b45ed7
+++ b/pyanaconda/install.py
b45ed7
@@ -253,24 +253,9 @@ def doInstall(storage, payload, ksdata, instClass):
b45ed7
         if iutil.getSysroot() != iutil.getTargetPhysicalRoot():
b45ed7
             blivet.setSysroot(iutil.getTargetPhysicalRoot(),
b45ed7
                               iutil.getSysroot())
b45ed7
-
b45ed7
-            # Now that we have the FS layout in the target, umount
b45ed7
-            # things that were in the legacy sysroot, and put them in
b45ed7
-            # the target root, except for the physical /.  First,
b45ed7
-            # unmount all target filesystems.
b45ed7
-            storage.umountFilesystems()
b45ed7
-
b45ed7
-            # Explicitly mount the root on the physical sysroot
b45ed7
-            rootmnt = storage.mountpoints.get('/')
b45ed7
-            rootmnt.setup()
b45ed7
-            rootmnt.format.setup(options=rootmnt.format.options, chroot=iutil.getTargetPhysicalRoot())
b45ed7
-
b45ed7
+            # Note this changed for RHEL 7.5; see comments in rpmostreepayload.py.
b45ed7
             payload.prepareMountTargets(storage)
b45ed7
 
b45ed7
-            # Everything else goes in the target root, including /boot
b45ed7
-            # since the bootloader code will expect to find /boot
b45ed7
-            # inside the chroot.
b45ed7
-            storage.mountFilesystems(skipRoot=True)
b45ed7
         storage.write()
b45ed7
 
b45ed7
     # Do bootloader.
b45ed7
diff --git a/pyanaconda/packaging/rpmostreepayload.py b/pyanaconda/packaging/rpmostreepayload.py
b45ed7
index da7650d3b..a472fa0bc 100644
b45ed7
--- a/pyanaconda/packaging/rpmostreepayload.py
b45ed7
+++ b/pyanaconda/packaging/rpmostreepayload.py
b45ed7
@@ -48,7 +48,7 @@ class RPMOSTreePayload(ArchivePayload):
b45ed7
         super(RPMOSTreePayload, self).__init__(data)
b45ed7
 
b45ed7
         self._base_remote_args = None
b45ed7
-        self._binds = []
b45ed7
+        self._internal_mounts = []
b45ed7
         self._sysroot_path = None
b45ed7
 
b45ed7
     @property
b45ed7
@@ -166,8 +166,21 @@ class RPMOSTreePayload(ArchivePayload):
b45ed7
         progress = OSTree.AsyncProgress.new()
b45ed7
         progress.connect('changed', self._pullProgressCb)
b45ed7
 
b45ed7
+        pull_opts = {'refs': GLib.Variant('as', [ostreesetup.ref])}
b45ed7
+        # If we're doing a kickstart, we can at least use the content as a reference:
b45ed7
+        # See <https://github.com/rhinstaller/anaconda/issues/1117>
b45ed7
+        # The first path here is used by <https://pagure.io/fedora-lorax-templates>
b45ed7
+        # and the second by <https://github.com/projectatomic/rpm-ostree-toolbox/>
b45ed7
+        if OSTree.check_version(2017, 8):
b45ed7
+            for path in ['/ostree/repo', '/install/ostree/repo']:
b45ed7
+                if os.path.isdir(path + '/objects'):
b45ed7
+                    pull_opts['localcache-repos'] = GLib.Variant('as', [path])
b45ed7
+                    break
b45ed7
+
b45ed7
         try:
b45ed7
-            repo.pull(ostreesetup.remote, [ostreesetup.ref], 0, progress, cancellable)
b45ed7
+            repo.pull_with_options(ostreesetup.remote,
b45ed7
+                                   GLib.Variant('a{sv}', pull_opts),
b45ed7
+                                   progress, cancellable)
b45ed7
         except GLib.GError as e:
b45ed7
             exn = PayloadInstallError("Failed to pull from repository: %s" % e)
b45ed7
             log.error(str(exn))
b45ed7
@@ -176,6 +189,7 @@ class RPMOSTreePayload(ArchivePayload):
b45ed7
                 iutil.ipmi_abort(scripts=self.data.scripts)
b45ed7
                 sys.exit(1)
b45ed7
 
b45ed7
+        log.info("ostree pull: " + (progress.get_status() or ""))
b45ed7
         progressQ.send_message(_("Preparing deployment of %s") % (ostreesetup.ref, ))
b45ed7
 
b45ed7
         self._safeExecWithRedirect("ostree",
b45ed7
@@ -213,25 +227,102 @@ class RPMOSTreePayload(ArchivePayload):
b45ed7
 
b45ed7
         mainctx.pop_thread_default()
b45ed7
 
b45ed7
+    def _setupInternalBindmount(self, src, dest=None,
b45ed7
+                                src_physical=True,
b45ed7
+                                bind_ro=False,
b45ed7
+                                recurse=True):
b45ed7
+        """Internal API for setting up bind mounts between the physical root and
b45ed7
+           sysroot, also ensures we track them in self._internal_mounts so we can
b45ed7
+           cleanly unmount them.
b45ed7
+
b45ed7
+           :param src: Source path, will be prefixed with physical or sysroot
b45ed7
+           :param dest: Destination, will be prefixed with sysroot (defaults to same as src)
b45ed7
+           :param src_physical: Prefix src with physical root
b45ed7
+           :param bind_ro: Make mount read-only
b45ed7
+           :param recurse: Use --rbind to recurse, otherwise plain --bind
b45ed7
+        """
b45ed7
+        # Default to the same basename
b45ed7
+        if dest is None:
b45ed7
+            dest = src
b45ed7
+        # Almost all of our mounts go from physical to sysroot
b45ed7
+        if src_physical:
b45ed7
+            src = iutil.getTargetPhysicalRoot() + src
b45ed7
+        else:
b45ed7
+            src = iutil.getSysroot() + src
b45ed7
+        # Canonicalize dest to the full path
b45ed7
+        dest = iutil.getSysroot() + dest
b45ed7
+        if bind_ro:
b45ed7
+            self._safeExecWithRedirect("mount",
b45ed7
+                                       ["--bind", src, src])
b45ed7
+            self._safeExecWithRedirect("mount",
b45ed7
+                                       ["--bind", "-o", "remount,ro", src, src])
b45ed7
+        else:
b45ed7
+            # Recurse for non-ro binds so we pick up sub-mounts
b45ed7
+            # like /sys/firmware/efi/efivars.
b45ed7
+            if recurse:
b45ed7
+                bindopt = '--rbind'
b45ed7
+            else:
b45ed7
+                bindopt = '--bind'
b45ed7
+            self._safeExecWithRedirect("mount",
b45ed7
+                                       [bindopt, src, dest])
b45ed7
+        self._internal_mounts.append(src if bind_ro else dest)
b45ed7
+
b45ed7
     def prepareMountTargets(self, storage):
b45ed7
         ostreesetup = self.data.ostreesetup
b45ed7
 
b45ed7
-        varroot = iutil.getTargetPhysicalRoot() + '/ostree/deploy/' + ostreesetup.osname + '/var'
b45ed7
-
b45ed7
-        # Set up bind mounts as if we've booted the target system, so
b45ed7
-        # that %post script work inside the target.
b45ed7
-        self._binds = [(iutil.getTargetPhysicalRoot(), iutil.getSysroot() + '/sysroot'),
b45ed7
-                       (iutil.getSysroot() + '/usr', None)]
b45ed7
-        # https://github.com/ostreedev/ostree/issues/855
b45ed7
+        # NOTE: This is different from Fedora. There, since since
b45ed7
+        # 664ef7b43f9102aa9332d0db5b7d13f8ece436f0 blivet now only sets up
b45ed7
+        # mounts in the physical root, and we set up bind mounts. But in RHEL7
b45ed7
+        # we tear down and set up the mounts in the sysroot, so this code
b45ed7
+        # doesn't need to do as much.
b45ed7
+
b45ed7
+        # Now that we have the FS layout in the target, umount
b45ed7
+        # things that were in the physical sysroot, and put them in
b45ed7
+        # the target root, *except* for the physical /.
b45ed7
+        storage.umountFilesystems()
b45ed7
+
b45ed7
+        # Explicitly mount the root on the physical sysroot, since that's
b45ed7
+        # how ostree works.
b45ed7
+        rootmnt = storage.mountpoints.get('/')
b45ed7
+        rootmnt.setup()
b45ed7
+        rootmnt.format.setup(options=rootmnt.format.options, chroot=iutil.getTargetPhysicalRoot())
b45ed7
+
b45ed7
+        # Everything else goes in the target root, including /boot
b45ed7
+        # since the bootloader code will expect to find /boot
b45ed7
+        # inside the chroot.
b45ed7
+        storage.mountFilesystems(skipRoot=True)
b45ed7
+
b45ed7
+        # We're done with blivet mounts; now set up binds as ostree does it at
b45ed7
+        # runtime.  We start with /usr being read-only.
b45ed7
+        self._setupInternalBindmount('/usr', bind_ro=True, src_physical=False)
b45ed7
+
b45ed7
+        # Handle /var; if the admin didn't specify a mount for /var, we need to
b45ed7
+        # do the default ostree one. See also
b45ed7
+        # <https://github.com/ostreedev/ostree/issues/855>.
b45ed7
+        varroot = '/ostree/deploy/' + ostreesetup.osname + '/var'
b45ed7
         if storage.mountpoints.get("/var") is None:
b45ed7
-            self._binds.append((varroot, iutil.getSysroot() + '/var'))
b45ed7
-
b45ed7
-        for (src, dest) in self._binds:
b45ed7
-            self._safeExecWithRedirect("mount",
b45ed7
-                                       ["--bind", src, dest if dest else src])
b45ed7
-            if dest is None:
b45ed7
-                self._safeExecWithRedirect("mount",
b45ed7
-                                           ["--bind", "-o", "ro", src, src])
b45ed7
+            self._setupInternalBindmount(varroot, dest='/var', recurse=False)
b45ed7
+
b45ed7
+        self._setupInternalBindmount("/", dest="/sysroot", recurse=False)
b45ed7
+
b45ed7
+        # Now that we have /var, start filling in any directories that may be
b45ed7
+        # required later there. We explicitly make /var/lib, since
b45ed7
+        # systemd-tmpfiles doesn't have a --prefix-only=/var/lib. We rely on
b45ed7
+        # 80-setfilecons.ks to set the label correctly.
b45ed7
+        iutil.mkdirChain(iutil.getSysroot() + '/var/lib')
b45ed7
+        # Next, run tmpfiles to make subdirectories of /var. We need this for
b45ed7
+        # both mounts like /home (really /var/home) and %post scripts might
b45ed7
+        # want to write to e.g. `/srv`, `/root`, `/usr/local`, etc. The
b45ed7
+        # /var/lib/rpm symlink is also critical for having e.g. `rpm -qa` work
b45ed7
+        # in %post. We don't iterate *all* tmpfiles because we don't have the
b45ed7
+        # matching NSS configuration inside Anaconda, and we can't "chroot" to
b45ed7
+        # get it because that would require mounting the API filesystems in the
b45ed7
+        # target.
b45ed7
+        for varsubdir in ('home', 'roothome', 'lib/rpm', 'opt', 'srv',
b45ed7
+                          'usrlocal', 'mnt', 'media', 'spool', 'spool/mail'):
b45ed7
+            self._safeExecWithRedirect("systemd-tmpfiles",
b45ed7
+                                       ["--create", "--boot", "--root=" + iutil.getSysroot(),
b45ed7
+                                        "--prefix=/var/" + varsubdir])
b45ed7
 
b45ed7
     def recreateInitrds(self, force=False):
b45ed7
         # For rpmostree payloads, we're replicating an initramfs from
b45ed7
@@ -312,6 +403,6 @@ class RPMOSTreePayload(ArchivePayload):
b45ed7
         # on inside either blivet (or systemd?) that's causing mounts inside
b45ed7
         # /mnt/sysimage/ostree/deploy/$x/sysroot/ostree/deploy
b45ed7
         # which is not what we want.
b45ed7
-        for (src, dest) in reversed(self._binds):
b45ed7
+        for path in reversed(self._internal_mounts):
b45ed7
             # Also intentionally ignore errors here
b45ed7
-            iutil.execWithRedirect("umount", ['-R', dest if dest else src])
b45ed7
+            iutil.execWithRedirect("umount", ['-R', path])
b45ed7
-- 
b45ed7
2.13.5
b45ed7