pmbootstrap-meow/pmb/install/blockdevice.py
Oliver Smith ef047137d0
install: rename --sdcard arg to --disk
Rename the argument, because any block device can be passed to the
argument. Use "disk", because the other short word "device" usually
means the target device/phone to install.

Keep --sdcard as alias for compatibility with existing scripts and
muscle memory.

Reviewed-by: Clayton Craft <clayton@craftyguy.net>
Link: https://lists.sr.ht/~postmarketos/pmbootstrap-devel/%3C20231119182302.2415-1-ollieparanoid@postmarketos.org%3E
2023-11-19 20:27:37 +01:00

145 lines
5.9 KiB
Python

# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
import os
import glob
import pmb.helpers.mount
import pmb.install.losetup
import pmb.helpers.cli
import pmb.config
def previous_install(args, path):
"""
Search the disk for possible existence of a previous installation of
pmOS. We temporarily mount the possible pmOS_boot partition as
/dev/diskp1 inside the native chroot to check the label from there.
:param path: path to disk block device (e.g. /dev/mmcblk0)
"""
label = ""
for blockdevice_outside in [f"{path}1", f"{path}p1"]:
if not os.path.exists(blockdevice_outside):
continue
blockdevice_inside = "/dev/diskp1"
pmb.helpers.mount.bind_file(args, blockdevice_outside,
args.work + '/chroot_native' +
blockdevice_inside)
try:
label = pmb.chroot.root(args, ["blkid", "-s", "LABEL",
"-o", "value",
blockdevice_inside],
output_return=True)
except RuntimeError:
logging.info("WARNING: Could not get block device label,"
" assume no previous installation on that partition")
pmb.helpers.run.root(args, ["umount", args.work + "/chroot_native" +
blockdevice_inside])
return "pmOS_boot" in label
def mount_disk(args, path):
"""
:param path: path to disk block device (e.g. /dev/mmcblk0)
"""
# Sanity checks
if not os.path.exists(path):
raise RuntimeError(f"The disk block device does not exist: {path}")
for path_mount in glob.glob(f"{path}*"):
if pmb.helpers.mount.ismount(path_mount):
raise RuntimeError(f"{path_mount} is mounted! Will not attempt to"
" format this!")
logging.info(f"(native) mount /dev/install (host: {path})")
pmb.helpers.mount.bind_file(args, path,
args.work + "/chroot_native/dev/install")
if previous_install(args, path):
if not pmb.helpers.cli.confirm(args, "WARNING: This device has a"
" previous installation of pmOS."
" CONTINUE?"):
raise RuntimeError("Aborted.")
else:
if not pmb.helpers.cli.confirm(args, f"EVERYTHING ON {path} WILL BE"
" ERASED! CONTINUE?"):
raise RuntimeError("Aborted.")
def create_and_mount_image(args, size_boot, size_root, size_reserve,
split=False):
"""
Create a new image file, and mount it as /dev/install.
:param size_boot: size of the boot partition in MiB
:param size_root: size of the root partition in MiB
:param size_reserve: empty partition between root and boot in MiB (pma#463)
:param split: create separate images for boot and root partitions
"""
# Short variables for paths
chroot = args.work + "/chroot_native"
img_path_prefix = "/home/pmos/rootfs/" + args.device
img_path_full = img_path_prefix + ".img"
img_path_boot = img_path_prefix + "-boot.img"
img_path_root = img_path_prefix + "-root.img"
# Umount and delete existing images
for img_path in [img_path_full, img_path_boot, img_path_root]:
outside = chroot + img_path
if os.path.exists(outside):
pmb.helpers.mount.umount_all(args, chroot + "/mnt")
pmb.install.losetup.umount(args, img_path)
pmb.chroot.root(args, ["rm", img_path])
# Make sure there is enough free space
size_mb = round(size_boot + size_reserve + size_root)
disk_data = os.statvfs(args.work)
free = round((disk_data.f_bsize * disk_data.f_bavail) / (1024**2))
if size_mb > free:
raise RuntimeError("Not enough free space to create rootfs image! "
f"(free: {free}M, required: {size_mb}M)")
# Create empty image files
pmb.chroot.user(args, ["mkdir", "-p", "/home/pmos/rootfs"])
size_mb_full = str(size_mb) + "M"
size_mb_boot = str(round(size_boot)) + "M"
size_mb_root = str(round(size_root)) + "M"
images = {img_path_full: size_mb_full}
if split:
images = {img_path_boot: size_mb_boot,
img_path_root: size_mb_root}
for img_path, size_mb in images.items():
logging.info(f"(native) create {os.path.basename(img_path)} "
f"({size_mb})")
pmb.chroot.root(args, ["truncate", "-s", size_mb, img_path])
# Mount to /dev/install
mount_image_paths = {img_path_full: "/dev/install"}
if split:
mount_image_paths = {img_path_boot: "/dev/installp1",
img_path_root: "/dev/installp2"}
for img_path, mount_point in mount_image_paths.items():
logging.info("(native) mount " + mount_point +
" (" + os.path.basename(img_path) + ")")
pmb.install.losetup.mount(args, img_path)
device = pmb.install.losetup.device_by_back_file(args, img_path)
pmb.helpers.mount.bind_file(args, device,
args.work + "/chroot_native" + mount_point)
def create(args, size_boot, size_root, size_reserve, split, disk):
"""
Create /dev/install (the "install blockdevice").
:param size_boot: size of the boot partition in MiB
:param size_root: size of the root partition in MiB
:param size_reserve: empty partition between root and boot in MiB (pma#463)
:param split: create separate images for boot and root partitions
:param disk: path to disk block device (e.g. /dev/mmcblk0) or None
"""
pmb.helpers.mount.umount_all(
args, args.work + "/chroot_native/dev/install")
if disk:
mount_disk(args, disk)
else:
create_and_mount_image(args, size_boot, size_root, size_reserve,
split)