osbuild/util: new module: chroot

New chroot utility module that sets up a tree with the necessary virtual
filesystems needed for running commands in the root tree in a similar
environment as they would run in the build root.

This is needed for some stages, but may also be used for all chroot
calls to unify the setup and teardown of the root environment.

The Chroot context class was previously part of the org.osbuild.dracut
stage, which was the first stage to need this setup.
This commit is contained in:
Achilleas Koutsou 2024-08-21 12:29:50 +02:00 committed by Tomáš Hozza
parent 8a9c6aba3c
commit 9edda1d163
2 changed files with 43 additions and 32 deletions

41
osbuild/util/chroot.py Normal file
View file

@ -0,0 +1,41 @@
import os
import subprocess
class Chroot:
"""
Sets up mounts for the virtual filesystems inside a root tree, preparing it for running commands using chroot. This
should be used whenever a stage needs to run a command against the root tree but doesn't support a --root option or
similar.
Cleans up mounts when done.
This mounts /proc, /dev, and /sys.
"""
def __init__(self, root: str):
self.root = root
def __enter__(self):
for d in ["/proc", "/dev", "/sys"]:
if not os.path.exists(self.root + d):
print(f"Making missing chroot directory: {d}")
os.makedirs(self.root + d)
subprocess.check_call(["/usr/bin/mount",
"-t", "proc",
"-o", "nosuid,noexec,nodev",
"proc", f"{self.root}/proc"])
subprocess.check_call(["/usr/bin/mount",
"-t", "devtmpfs",
"-o", "mode=0755,noexec,nosuid,strictatime",
"devtmpfs", f"{self.root}/dev"])
subprocess.check_call(["/usr/bin/mount",
"-t", "sysfs",
"-o", "nosuid,noexec,nodev",
"sysfs", f"{self.root}/sys"])
return self
def __exit__(self, exc_type, exc_value, tracebk):
for d in ["/proc", "/dev", "/sys"]:
subprocess.check_call(["/usr/bin/umount", self.root + d])

View file

@ -1,9 +1,9 @@
#!/usr/bin/python3
import os
import subprocess
import sys
import osbuild.api
from osbuild.util.chroot import Chroot
def yesno(name: str, value: bool) -> str:
@ -11,36 +11,6 @@ def yesno(name: str, value: bool) -> str:
return f"--{prefix}{name}"
class DracutMounts:
"""
Setup the mounts inside a chroot root directory, which will be used
for running dracut inside it. Cleanup mounts when done.
This mounts /proc, /dev, and /sys.
"""
def __init__(self, root: str):
self.root = root
def __enter__(self):
for d in ["/proc", "/dev", "/sys"]:
if not os.path.exists(self.root + d):
print(f"Making missing dracut chroot directory: {d}")
os.makedirs(self.root + d)
subprocess.check_call(["/usr/bin/mount", "-t", "proc", "-o",
"nosuid,noexec,nodev", "proc", f"{self.root}/proc"])
subprocess.check_call(["/usr/bin/mount", "-t", "devtmpfs", "-o",
"mode=0755,noexec,nosuid,strictatime", "devtmpfs", f"{self.root}/dev"])
subprocess.check_call(["/usr/bin/mount", "-t", "sysfs", "-o",
"nosuid,noexec,nodev", "sysfs", f"{self.root}/sys"])
return self
def __exit__(self, exc_type, exc_value, tracebk):
for d in ["/proc", "/dev", "/sys"]:
subprocess.check_call(["/usr/bin/umount", self.root + d])
# pylint: disable=too-many-branches
def main(tree, options):
kernels = options["kernel"]
@ -113,7 +83,7 @@ def main(tree, options):
if initoverlayfs:
initfs_bin = "/usr/bin/initoverlayfs-install"
with DracutMounts(tree):
with Chroot(tree):
subprocess.run(["/usr/sbin/chroot", tree, initfs_bin,
"--no-hostonly",
"--kver", kver]