# Copyright 2023 Oliver Smith # SPDX-License-Identifier: GPL-3.0-or-later from pmb.core.pkgrepo import pkgrepo_default_path from pmb.helpers import logging import os from pathlib import Path import pmb.chroot.binfmt import pmb.config import pmb.helpers.run import pmb.helpers.mount from pmb.core import Chroot from pmb.core.context import get_context from pmb.init import sandbox def mount_dev_tmpfs(chroot: Chroot = Chroot.native()) -> None: """ Mount tmpfs inside the chroot's dev folder to make sure we can create device nodes, even if the filesystem of the work folder does not support it. """ # Do nothing when it is already mounted # dev = chroot / "dev" # if pmb.helpers.mount.ismount(dev): # return logging.info(f"mount_dev_tmpfs({chroot})") # Use sandbox to set up /dev inside the chroot ttyname = os.ttyname(2) if os.isatty(2) else "" devop = sandbox.DevOperation(ttyname, "/dev") devop.execute("/", str(chroot.path)) def mount(chroot: Chroot): # Mount tmpfs as the chroot's /dev chroot.path.mkdir(exist_ok=True) mount_dev_tmpfs(chroot) # Get all mountpoints arch = chroot.arch channel = pmb.config.pmaports.read_config(pkgrepo_default_path())["channel"] mountpoints: dict[Path, Path] = {} for src_template, target_template in pmb.config.chroot_mount_bind.items(): src_template = src_template.replace("$CACHE", os.fspath(get_context().config.cache)) src_template = src_template.replace("$ARCH", str(arch)) src_template = src_template.replace("$CHANNEL", channel) mountpoints[Path(src_template).resolve()] = Path(target_template) # Mount if necessary for source, target in mountpoints.items(): target_outer = chroot / target if not pmb.helpers.mount.ismount(target_outer): pmb.helpers.mount.bind(source, target_outer) # Set up binfmt if not arch.cpu_emulation_required(): return arch_qemu = arch.qemu() # mount --bind the qemu-user binary pmb.chroot.binfmt.register(arch) pmb.helpers.mount.bind_file( Chroot.native() / f"usr/bin/qemu-{arch_qemu}", chroot / f"usr/bin/qemu-{arch_qemu}-static", create_folders=True, ) def mount_native_into_foreign(chroot: Chroot) -> None: source = Chroot.native().path target = chroot / "native" pmb.helpers.mount.bind(source, target) musl = next(source.glob("lib/ld-musl-*.so.1")).name musl_link = chroot / "lib" / musl if not musl_link.is_symlink(): pmb.helpers.run.root(["ln", "-s", "/native/lib/" + musl, musl_link]) # pmb.helpers.run.root(["ln", "-sf", "/native/usr/bin/pigz", "/usr/local/bin/pigz"]) def remove_mnt_pmbootstrap(chroot: Chroot) -> None: """Safely remove /cache directories from the chroot, without running rm -r as root and potentially removing data inside the mountpoint in case it was still mounted (bug in pmbootstrap, or user ran pmbootstrap 2x in parallel). This is similar to running 'rm -r -d', but we don't assume that the host's rm has the -d flag (busybox does not).""" mnt_dir = chroot / "work" if not mnt_dir.exists(): return for path in [*mnt_dir.glob("*"), mnt_dir]: path.rmdir() if mnt_dir.exists(): raise RuntimeError("Failed to remove /cache!") def umount(chroot: Chroot) -> None: """Unmount all bind mounts inside a chroot.""" if chroot.path.exists(): pmb.helpers.mount.umount_all(chroot.path)