forked from Mirror/pmbootstrap
chroot: allow mounting the device rootfs (MR 2252)
Add a new flag --image which can be used to mount the rootfs generated with "pmbootstrap install". For now this is quite limited in scope. But it's enough to allow for building a package, updating it in the QEMU image, and then booting it. The major "gotcha" with this is that the QEMU uses the kernel and initramfs from the device chroot unless you run it with --efi. Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
This commit is contained in:
parent
d1a86fe702
commit
f331b95824
8 changed files with 55 additions and 11 deletions
|
@ -1,5 +1,6 @@
|
|||
# Copyright 2023 Oliver Smith
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from pmb.core.chroot import ChrootType
|
||||
from pmb.core.pkgrepo import pkgrepo_default_path
|
||||
from pmb.helpers import logging
|
||||
import os
|
||||
|
@ -9,11 +10,36 @@ import pmb.chroot.binfmt
|
|||
import pmb.config
|
||||
import pmb.helpers.run
|
||||
import pmb.parse
|
||||
import pmb.install.losetup
|
||||
import pmb.helpers.mount
|
||||
from pmb.core import Chroot
|
||||
from pmb.core.context import get_context
|
||||
|
||||
|
||||
def mount_chroot_image(chroot: Chroot):
|
||||
"""Mount an IMAGE type chroot, to modify an existing rootfs image. This
|
||||
doesn't support split images yet!"""
|
||||
# Make sure everything is nicely unmounted just to be super safe
|
||||
# this is definitely overkill
|
||||
pmb.chroot.shutdown()
|
||||
pmb.install.losetup.detach_all()
|
||||
|
||||
chroot_native = Chroot.native()
|
||||
pmb.chroot.init(chroot_native)
|
||||
|
||||
loopdev = pmb.install.losetup.mount(Path("/") / Path(chroot.name).relative_to(chroot_native.path))
|
||||
pmb.helpers.mount.bind_file(loopdev, chroot_native / "dev/install")
|
||||
# Set up device mapper bits
|
||||
pmb.chroot.root(["kpartx", "-u", "/dev/install"], chroot_native)
|
||||
chroot.path.mkdir(exist_ok=True)
|
||||
# # The name of the IMAGE chroot is the path to the rootfs image
|
||||
pmb.helpers.run.root(["mount", "/dev/mapper/install2", chroot.path])
|
||||
pmb.helpers.run.root(["mount", "/dev/mapper/install1", chroot.path / "boot"])
|
||||
|
||||
pmb.config.workdir.chroot_save_init(chroot)
|
||||
|
||||
logging.info(f"({chroot}) mounted {chroot.name}")
|
||||
|
||||
def create_device_nodes(chroot: Chroot):
|
||||
"""
|
||||
Create device nodes for null, zero, full, random, urandom in the chroot.
|
||||
|
@ -80,6 +106,9 @@ def mount_dev_tmpfs(chroot: Chroot=Chroot.native()):
|
|||
|
||||
|
||||
def mount(chroot: Chroot):
|
||||
if chroot.type == ChrootType.IMAGE and not pmb.mount.ismount(chroot.path):
|
||||
mount_chroot_image(chroot)
|
||||
|
||||
# Mount tmpfs as the chroot's /dev
|
||||
mount_dev_tmpfs(chroot)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ class ChrootType(enum.Enum):
|
|||
BUILDROOT = "buildroot"
|
||||
INSTALLER = "installer"
|
||||
NATIVE = "native"
|
||||
IMAGE = "image"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
@ -65,9 +66,12 @@ class Chroot:
|
|||
raise ValueError(f"The native suffix can't have a name but got: "
|
||||
f"'{self.__name}'")
|
||||
|
||||
if self.__type == ChrootType.IMAGE and not Path(self.__name).exists():
|
||||
raise ValueError(f"Image file '{self.__name}' does not exist")
|
||||
|
||||
|
||||
def __str__(self) -> str:
|
||||
if len(self.__name) > 0:
|
||||
if len(self.__name) > 0 and self.type != ChrootType.IMAGE:
|
||||
return f"{self.__type.value}_{self.__name}"
|
||||
else:
|
||||
return self.__type.value
|
||||
|
|
|
@ -63,11 +63,15 @@ def _parse_flavor(device: str, autoinstall=True):
|
|||
|
||||
|
||||
def _parse_suffix(args: PmbArgs) -> Chroot:
|
||||
deviceinfo = pmb.parse.deviceinfo()
|
||||
if args.image:
|
||||
rootfs = Chroot.native() / f"home/pmos/rootfs/{deviceinfo.codename}.img"
|
||||
return Chroot(ChrootType.IMAGE, str(rootfs))
|
||||
if "rootfs" in args and args.rootfs:
|
||||
return Chroot(ChrootType.ROOTFS, get_context().config.device)
|
||||
elif args.buildroot:
|
||||
if args.buildroot == "device":
|
||||
return Chroot.buildroot(pmb.parse.deviceinfo().arch)
|
||||
return Chroot.buildroot(deviceinfo.arch)
|
||||
else:
|
||||
return Chroot.buildroot(Arch.from_str(args.buildroot))
|
||||
elif args.suffix:
|
||||
|
@ -168,12 +172,15 @@ def chroot(args: PmbArgs):
|
|||
chroot = _parse_suffix(args)
|
||||
user = args.user
|
||||
if (user and chroot != Chroot.native() and
|
||||
not chroot.type == ChrootType.BUILDROOT):
|
||||
chroot.type not in [ChrootType.BUILDROOT, ChrootType.IMAGE]):
|
||||
raise RuntimeError("--user is only supported for native or"
|
||||
" buildroot_* chroots.")
|
||||
if args.xauth and chroot != Chroot.native():
|
||||
raise RuntimeError("--xauth is only supported for native chroot.")
|
||||
|
||||
if chroot.type == ChrootType.IMAGE:
|
||||
pmb.chroot.mount(chroot)
|
||||
|
||||
# apk: check minimum version, install packages
|
||||
pmb.chroot.apk.check_min_version(chroot)
|
||||
if args.add:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright 2023 Oliver Smith
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from typing import Optional
|
||||
from pmb.core.config import Config
|
||||
from pmb.helpers import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
|
|
@ -46,14 +46,8 @@ def mount(img_path: Path):
|
|||
losetup_cmd += ["-b", str(int(sector_size))]
|
||||
|
||||
pmb.chroot.root(losetup_cmd, check=False)
|
||||
try:
|
||||
device_by_back_file(img_path)
|
||||
return
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
# Failure: raise exception
|
||||
raise RuntimeError(f"Failed to mount loop device: {img_path}")
|
||||
return device_by_back_file(img_path)
|
||||
|
||||
|
||||
def device_by_back_file(back_file: Path) -> Path:
|
||||
|
@ -100,5 +94,6 @@ def detach_all():
|
|||
for loopdevice in losetup["loopdevices"]:
|
||||
print(loopdevice["back-file"])
|
||||
if Path(loopdevice["back-file"]).is_relative_to(work):
|
||||
pmb.chroot.root(["losetup", "-d", loopdevice["name"]])
|
||||
pmb.helpers.run.root(["kpartx", "-d", loopdevice["name"]], check=False)
|
||||
pmb.helpers.run.root(["losetup", "-d", loopdevice["name"]])
|
||||
return
|
||||
|
|
|
@ -812,6 +812,8 @@ def get_parser():
|
|||
" 'stdout', 'interactive', 'tui' (default),"
|
||||
" 'background'. Details: pmb/helpers/run_core.py",
|
||||
default="tui")
|
||||
chroot.add_argument("--image", help="Mount the rootfs image and treat"
|
||||
" it like a normal chroot.", action="store_true")
|
||||
chroot.add_argument("command", default=["sh", "-i"], help="command"
|
||||
" to execute inside the chroot. default: sh",
|
||||
nargs='*')
|
||||
|
|
|
@ -20,6 +20,7 @@ import pmb.chroot.other
|
|||
import pmb.chroot.initfs
|
||||
import pmb.config
|
||||
import pmb.config.pmaports
|
||||
import pmb.install.losetup
|
||||
from pmb.types import PathString, PmbArgs
|
||||
import pmb.helpers.run
|
||||
import pmb.parse.cpuinfo
|
||||
|
@ -345,6 +346,10 @@ def run(args: PmbArgs):
|
|||
"and select the 'qemu' vendor.")
|
||||
arch = pmb.parse.deviceinfo().arch
|
||||
|
||||
# Make sure the rootfs image isn't mounted
|
||||
pmb.mount.umount_all(Chroot(ChrootType.IMAGE, "").path)
|
||||
pmb.install.losetup.detach_all()
|
||||
|
||||
img_path = system_image(device)
|
||||
img_path_2nd = None
|
||||
if args.second_storage:
|
||||
|
|
|
@ -78,6 +78,7 @@ class PmbArgs(Namespace):
|
|||
host: str
|
||||
host_qemu: str
|
||||
image_size: str
|
||||
image: bool
|
||||
install_base: str
|
||||
install_blockdev: str
|
||||
install_cgpt: str
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue