forked from Mirror/pmbootstrap
This can be used when building images for generic device targets that support devices with different sector size requirements. For example, trailblazer prebuilts are currently expected to be flashed to a USB drive where a 4096 sector size would be unsuitable since the bootloader wouldn't detect it. But when building for a Qualcomm phone, one would use --split and --sector-size to build the root and boot partitions with a 4k sector size which is appropriate to the UFS storage. This flag could also be used by BPO to build both variants. Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
105 lines
3.1 KiB
Python
105 lines
3.1 KiB
Python
# Copyright 2023 Oliver Smith
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
import json
|
|
from pathlib import Path
|
|
from pmb.core.context import get_context
|
|
from pmb.helpers import logging
|
|
import time
|
|
|
|
from pmb.types import PathString
|
|
import pmb.helpers.mount
|
|
import pmb.helpers.run
|
|
import pmb.chroot
|
|
from pmb.core import Chroot
|
|
|
|
|
|
def init():
|
|
if not Path("/sys/module/loop").is_dir():
|
|
pmb.helpers.run.root(["modprobe", "loop"])
|
|
for loopdevice in Path("/dev/").glob("loop*"):
|
|
if loopdevice.is_dir():
|
|
continue
|
|
pmb.helpers.mount.bind_file(loopdevice, Chroot.native() / loopdevice)
|
|
|
|
|
|
def mount(img_path: Path, _sector_size: int | None = None):
|
|
"""
|
|
:param img_path: Path to the img file inside native chroot.
|
|
"""
|
|
logging.debug(f"(native) mount {img_path} (loop)")
|
|
|
|
# Try to mount multiple times (let the kernel module initialize #1594)
|
|
for i in range(0, 5):
|
|
# Retry
|
|
if i > 0:
|
|
logging.debug("loop module might not be initialized yet, retry in" " one second...")
|
|
time.sleep(1)
|
|
|
|
# Mount and return on success
|
|
init()
|
|
|
|
sector_size = None
|
|
if _sector_size:
|
|
sector_size = str(_sector_size)
|
|
sector_size = sector_size or pmb.parse.deviceinfo().rootfs_image_sector_size
|
|
|
|
losetup_cmd: list[PathString] = ["losetup", "-f", img_path]
|
|
if sector_size:
|
|
losetup_cmd += ["-b", str(int(sector_size))]
|
|
|
|
pmb.chroot.root(losetup_cmd, check=False)
|
|
|
|
try:
|
|
return device_by_back_file(img_path)
|
|
except RuntimeError as e:
|
|
if i == 4:
|
|
raise e
|
|
pass
|
|
|
|
|
|
def device_by_back_file(back_file: Path) -> Path:
|
|
"""
|
|
Get the /dev/loopX device that points to a specific image file.
|
|
"""
|
|
|
|
# Get list from losetup
|
|
losetup_output = pmb.chroot.root(["losetup", "--json", "--list"], output_return=True)
|
|
if not losetup_output:
|
|
raise RuntimeError("losetup failed")
|
|
|
|
# Find the back_file
|
|
losetup = json.loads(losetup_output)
|
|
for loopdevice in losetup["loopdevices"]:
|
|
if Path(loopdevice["back-file"]) == back_file:
|
|
return Path(loopdevice["name"])
|
|
raise RuntimeError(f"Failed to find loop device for {back_file}")
|
|
|
|
|
|
def umount(img_path: Path):
|
|
"""
|
|
:param img_path: Path to the img file inside native chroot.
|
|
"""
|
|
device: Path
|
|
try:
|
|
device = device_by_back_file(img_path)
|
|
except RuntimeError:
|
|
return
|
|
logging.debug(f"(native) umount {device}")
|
|
pmb.chroot.root(["losetup", "-d", device])
|
|
|
|
|
|
def detach_all():
|
|
"""
|
|
Detach all loop devices used by pmbootstrap
|
|
"""
|
|
losetup_output = pmb.helpers.run.root(["losetup", "--json", "--list"], output_return=True)
|
|
if not losetup_output:
|
|
return
|
|
losetup = json.loads(losetup_output)
|
|
work = get_context().config.work
|
|
for loopdevice in losetup["loopdevices"]:
|
|
print(loopdevice["back-file"])
|
|
if Path(loopdevice["back-file"]).is_relative_to(work):
|
|
pmb.helpers.run.root(["kpartx", "-d", loopdevice["name"]], check=False)
|
|
pmb.helpers.run.root(["losetup", "-d", loopdevice["name"]])
|
|
return
|