pmbootstrap-meow/pmb/chroot/initfs.py
Caleb Connolly 198f302a36
treewide: add a Chroot type and adopt pathlib.Path (MR 2252)
Introduce a new module: pmb.core to contain explicitly typed pmbootstrap
API. The first component being Suffix and SuffixType. This explicitly
defines what suffixes are possible, future changes should aim to further
constrain this API (e.g. by validating against available device
codenames or architectures for buildroot suffixes).

Additionally, migrate the entire codebase over to using pathlib.Path.
This is a relatively new part of the Python standard library that uses a
more object oriented model for path handling. It also uses strong type
hinting and has other features that make it much cleaner and easier to
work with than pure f-strings. The Chroot class overloads the "/"
operator the same way the Path object does, allowing one to write paths
relative to a given chroot as:

builddir = chroot / "home/pmos/build"

The Chroot class also has a string representation ("native", or
"rootfs_valve-jupiter"), and a .path property for directly accessing the
absolute path (as a Path object).

The general idea here is to encapsulate common patterns into type hinted
code, and gradually reduce the amount of assumptions made around the
codebase so that future changes are easier to implement.

As the chroot suffixes are now part of the Chroot class, we also
implement validation for them, this encodes the rules on suffix naming
and will cause a runtime exception if a suffix doesn't follow the rules.
2024-06-23 12:38:37 +02:00

123 lines
4.4 KiB
Python

# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
from pathlib import Path
import pmb.chroot.initfs_hooks
import pmb.chroot.other
import pmb.chroot.apk
import pmb.config.pmaports
from pmb.core.types import PmbArgs
import pmb.helpers.cli
from pmb.core import Chroot
def build(args: PmbArgs, flavor, chroot: Chroot):
# Update mkinitfs and hooks
pmb.chroot.apk.install(args, ["postmarketos-mkinitfs"], chroot)
pmb.chroot.initfs_hooks.update(args, chroot)
pmaports_cfg = pmb.config.pmaports.read_config(args)
# Call mkinitfs
logging.info(f"({chroot}) mkinitfs {flavor}")
if pmaports_cfg.get("supported_mkinitfs_without_flavors", False):
pmb.chroot.root(args, ["mkinitfs"], chroot)
else:
release_file = (chroot / "usr/share/kernel" / flavor / "kernel.release")
with release_file.open() as handle:
release = handle.read().rstrip()
pmb.chroot.root(args, ["mkinitfs", "-o",
f"/boot/initramfs-{flavor}", release],
chroot)
def extract(args: PmbArgs, flavor, chroot: Chroot, extra=False):
"""
Extract the initramfs to /tmp/initfs-extracted or the initramfs-extra to
/tmp/initfs-extra-extracted and return the outside extraction path.
"""
# Extraction folder
inside = "/tmp/initfs-extracted"
pmaports_cfg = pmb.config.pmaports.read_config(args)
if pmaports_cfg.get("supported_mkinitfs_without_flavors", False):
initfs_file = "/boot/initramfs"
else:
initfs_file = f"/boot/initramfs-${flavor}"
if extra:
inside = "/tmp/initfs-extra-extracted"
initfs_file += "-extra"
outside = chroot / inside
if outside.exists():
if not pmb.helpers.cli.confirm(args, f"Extraction folder {outside}"
" already exists."
" Do you want to overwrite it?"):
raise RuntimeError("Aborted!")
pmb.chroot.root(args, ["rm", "-r", inside], chroot)
# Extraction script (because passing a file to stdin is not allowed
# in pmbootstrap's chroot/shell functions for security reasons)
with (chroot / "tmp/_extract.sh").open("w") as handle:
handle.write(
"#!/bin/sh\n"
f"cd {inside} && cpio -i < _initfs\n")
# Extract
commands = [["mkdir", "-p", inside],
["cp", initfs_file, f"{inside}/_initfs.gz"],
["gzip", "-d", f"{inside}/_initfs.gz"],
["cat", "/tmp/_extract.sh"], # for the log
["sh", "/tmp/_extract.sh"],
["rm", "/tmp/_extract.sh", f"{inside}/_initfs"]
]
for command in commands:
pmb.chroot.root(args, command, chroot)
# Return outside path for logging
return outside
def ls(args: PmbArgs, flavor, suffix, extra=False):
tmp = "/tmp/initfs-extracted"
if extra:
tmp = "/tmp/initfs-extra-extracted"
extract(args, flavor, suffix, extra)
pmb.chroot.root(args, ["ls", "-lahR", "."], suffix, Path(tmp), "stdout")
pmb.chroot.root(args, ["rm", "-r", tmp], suffix)
def frontend(args: PmbArgs):
# Find the appropriate kernel flavor
chroot = Chroot.rootfs(args.device)
flavor = pmb.chroot.other.kernel_flavor_installed(args, chroot)
# Handle initfs actions
action = args.action_initfs
if action == "build":
build(args, flavor, chroot)
elif action == "extract":
dir = extract(args, flavor, chroot)
logging.info(f"Successfully extracted initramfs to: {dir}")
dir_extra = extract(args, flavor, chroot, True)
logging.info(f"Successfully extracted initramfs-extra to: {dir_extra}")
elif action == "ls":
logging.info("*** initramfs ***")
ls(args, flavor, chroot)
logging.info("*** initramfs-extra ***")
ls(args, flavor, chroot, True)
# Handle hook actions
elif action == "hook_ls":
pmb.chroot.initfs_hooks.ls(args, chroot)
else:
if action == "hook_add":
pmb.chroot.initfs_hooks.add(args, args.hook, chroot)
elif action == "hook_del":
pmb.chroot.initfs_hooks.delete(args, args.hook, chroot)
# Rebuild the initfs after adding/removing a hook
build(args, flavor, chroot)
if action in ["ls", "extract"]:
link = "https://wiki.postmarketos.org/wiki/Initramfs_development"
logging.info(f"See also: <{link}>")