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