- Add optional bind_mounts parameter to __init__ method; - Enhanced methods to accept an optional `bind_mounts`. This allows for more flexible for configurations when setting up bind mounts. Signed-off-by: Renata Ravanelli <rravanel@redhat.com>
61 lines
2.3 KiB
Python
61 lines
2.3 KiB
Python
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, bind_mounts=None):
|
|
self.root = root
|
|
self._bind_mounts = bind_mounts or []
|
|
|
|
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.run(["mount", "-t", "proc", "-o", "nosuid,noexec,nodev",
|
|
"proc", f"{self.root}/proc"],
|
|
check=True)
|
|
|
|
subprocess.run(["mount", "-t", "devtmpfs", "-o", "mode=0755,noexec,nosuid,strictatime",
|
|
"devtmpfs", f"{self.root}/dev"],
|
|
check=True)
|
|
|
|
subprocess.run(["mount", "-t", "sysfs", "-o", "nosuid,noexec,nodev",
|
|
"sysfs", f"{self.root}/sys"],
|
|
check=True)
|
|
|
|
for d in self._bind_mounts:
|
|
target_path = os.path.join(self.root, d.lstrip("/"))
|
|
if not os.path.exists(target_path):
|
|
print(f"Making missing chroot directory: {d}")
|
|
os.makedirs(target_path)
|
|
subprocess.run(["mount", "--rbind", d, target_path], check=True)
|
|
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_value, tracebk):
|
|
failed_umounts = []
|
|
for d in ["/proc", "/dev", "/sys"]:
|
|
if subprocess.run(["umount", "--lazy", self.root + d], check=False).returncode != 0:
|
|
failed_umounts.append(d)
|
|
for d in self._bind_mounts[::-1]:
|
|
target_path = os.path.join(self.root, d.lstrip("/"))
|
|
if subprocess.run(["umount", "--lazy", target_path], check=False).returncode != 0:
|
|
failed_umounts.append(d)
|
|
if failed_umounts:
|
|
print(f"Error unmounting paths from chroot: {failed_umounts}")
|
|
|
|
def run(self, cmd, **kwargs):
|
|
cmd = ["chroot", self.root] + cmd
|
|
# pylint: disable=subprocess-run-check
|
|
return subprocess.run(cmd, **kwargs) # noqa: PLW1510
|