forked from Mirror/pmbootstrap
pmb/install: Add option to create single combined boot/root partition
With recent rework in postmarketos-initramfs, we no longer need to mount the /boot partition in the initramfs (assuming initramfs-extra is not used). On devices that boot without accessing the boot file system (e.g. Android boot images, fastboot, ...), that makes it possible to install postmarketOS on a single (potentially encrypted) partition that contains both root (/) and /boot files. This avoids the extra complexity of the subpartition setup we usually use on such devices, and also avoids having to flash two partitions (when using --split to avoid the subpartitions). Add a --single-partition option to pmbootstrap install that allows installing postmarketOS in this mode. For now this is just an option that must be selected explicitly, in the future we could choose to make this the default for Android-based devices with a large enough boot partition. Part-of: https://gitlab.postmarketos.org/postmarketOS/pmbootstrap/-/merge_requests/2512
This commit is contained in:
parent
ec0163ce63
commit
e9038e50d6
4 changed files with 88 additions and 38 deletions
|
@ -810,27 +810,30 @@ def get_uuid(args: PmbArgs, partition: Path) -> str:
|
|||
).rstrip()
|
||||
|
||||
|
||||
def create_crypttab(args: PmbArgs, layout: PartitionLayout, chroot: Chroot) -> None:
|
||||
def create_crypttab(args: PmbArgs, layout: PartitionLayout | None, chroot: Chroot) -> None:
|
||||
"""
|
||||
Create /etc/crypttab config
|
||||
|
||||
:param layout: partition layout from get_partition_layout()
|
||||
:param layout: partition layout from get_partition_layout() or None
|
||||
:param suffix: of the chroot, which crypttab will be created to
|
||||
"""
|
||||
if layout:
|
||||
root_dev = Path(f"/dev/installp{layout['root']}")
|
||||
else:
|
||||
root_dev = Path("/dev/install")
|
||||
|
||||
luks_uuid = get_uuid(args, Path("/dev") / f"installp{layout['root']}")
|
||||
|
||||
luks_uuid = get_uuid(args, root_dev)
|
||||
crypttab = f"root UUID={luks_uuid} none luks\n"
|
||||
|
||||
(chroot / "tmp/crypttab").open("w").write(crypttab)
|
||||
pmb.chroot.root(["mv", "/tmp/crypttab", "/etc/crypttab"], chroot)
|
||||
|
||||
|
||||
def create_fstab(args: PmbArgs, layout: PartitionLayout, chroot: Chroot) -> None:
|
||||
def create_fstab(args: PmbArgs, layout: PartitionLayout | None, chroot: Chroot) -> None:
|
||||
"""
|
||||
Create /etc/fstab config
|
||||
|
||||
:param layout: partition layout from get_partition_layout()
|
||||
:param layout: partition layout from get_partition_layout() or None
|
||||
:param chroot: of the chroot, which fstab will be created to
|
||||
"""
|
||||
|
||||
|
@ -839,19 +842,16 @@ def create_fstab(args: PmbArgs, layout: PartitionLayout, chroot: Chroot) -> None
|
|||
if args.on_device_installer and chroot.type == ChrootType.ROOTFS:
|
||||
return
|
||||
|
||||
if layout:
|
||||
boot_dev = Path(f"/dev/installp{layout['boot']}")
|
||||
root_dev = Path(f"/dev/installp{layout['root']}")
|
||||
else:
|
||||
boot_dev = None
|
||||
root_dev = Path("/dev/install")
|
||||
|
||||
boot_mount_point = f"UUID={get_uuid(args, boot_dev)}"
|
||||
root_mount_point = (
|
||||
"/dev/mapper/root" if args.full_disk_encryption else f"UUID={get_uuid(args, root_dev)}"
|
||||
)
|
||||
|
||||
boot_options = "nodev,nosuid,noexec"
|
||||
boot_filesystem = pmb.parse.deviceinfo().boot_filesystem or "ext2"
|
||||
if boot_filesystem in ("fat16", "fat32"):
|
||||
boot_filesystem = "vfat"
|
||||
boot_options += ",umask=0077,nosymfollow,codepage=437,iocharset=ascii"
|
||||
root_filesystem = pmb.install.get_root_filesystem(args)
|
||||
|
||||
if root_filesystem == "btrfs":
|
||||
|
@ -864,17 +864,23 @@ def create_fstab(args: PmbArgs, layout: PartitionLayout, chroot: Chroot) -> None
|
|||
{root_mount_point} /srv btrfs subvol=@srv,compress=zstd:2,ssd 0 0
|
||||
{root_mount_point} /var btrfs subvol=@var,ssd 0 0
|
||||
{root_mount_point} /.snapshots btrfs subvol=@snapshots,compress=zstd:2,ssd 0 0
|
||||
|
||||
{boot_mount_point} /boot {boot_filesystem} {boot_options} 0 0
|
||||
""".lstrip()
|
||||
|
||||
else:
|
||||
fstab = f"""
|
||||
# <file system> <mount point> <type> <options> <dump> <pass>
|
||||
{root_mount_point} / {root_filesystem} defaults 0 0
|
||||
{boot_mount_point} /boot {boot_filesystem} {boot_options} 0 0
|
||||
""".lstrip()
|
||||
|
||||
if boot_dev:
|
||||
boot_mount_point = f"UUID={get_uuid(args, boot_dev)}"
|
||||
boot_options = "nodev,nosuid,noexec"
|
||||
boot_filesystem = pmb.parse.deviceinfo().boot_filesystem or "ext2"
|
||||
if boot_filesystem in ("fat16", "fat32"):
|
||||
boot_filesystem = "vfat"
|
||||
boot_options += ",umask=0077,nosymfollow,codepage=437,iocharset=ascii"
|
||||
fstab += f"{boot_mount_point} /boot {boot_filesystem} {boot_options} 0 0\n"
|
||||
|
||||
with (chroot / "tmp/fstab").open("w") as f:
|
||||
f.write(fstab)
|
||||
pmb.chroot.root(["mv", "/tmp/fstab", "/etc/fstab"], chroot)
|
||||
|
@ -889,6 +895,7 @@ def install_system_image(
|
|||
boot_label: str = "pmOS_boot",
|
||||
root_label: str = "pmOS_root",
|
||||
split: bool = False,
|
||||
single_partition: bool = False,
|
||||
disk: Path | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
|
@ -908,12 +915,15 @@ def install_system_image(
|
|||
logging.info(f"*** ({step}/{steps}) PREPARE INSTALL BLOCKDEVICE ***")
|
||||
pmb.helpers.mount.umount_all(chroot.path)
|
||||
(size_boot, size_root) = get_subpartitions_size(chroot)
|
||||
if not single_partition:
|
||||
layout = get_partition_layout(
|
||||
size_reserve, bool(pmb.parse.deviceinfo().cgpt_kpart and args.install_cgpt)
|
||||
)
|
||||
else:
|
||||
layout = None
|
||||
if not args.rsync:
|
||||
pmb.install.blockdevice.create(args, size_boot, size_root, size_reserve, split, disk)
|
||||
if not split:
|
||||
if not split and layout:
|
||||
if pmb.parse.deviceinfo().cgpt_kpart and args.install_cgpt:
|
||||
pmb.install.partition_cgpt(layout, size_boot, size_reserve)
|
||||
else:
|
||||
|
@ -922,7 +932,8 @@ def install_system_image(
|
|||
# Inform kernel about changed partition table in case parted couldn't
|
||||
pmb.chroot.root(["partprobe", "/dev/install"], check=False)
|
||||
|
||||
if not split:
|
||||
if not split and not single_partition:
|
||||
assert layout # Initialized above for not single_partition case (mypy needs this)
|
||||
pmb.install.partitions_mount(device, layout, disk)
|
||||
|
||||
pmb.install.format(args, layout, boot_label, root_label, disk)
|
||||
|
@ -955,7 +966,8 @@ def install_system_image(
|
|||
|
||||
# Don't try to embed firmware and cgpt on split images since there's no
|
||||
# place to put it and it will end up in /dev of the chroot instead
|
||||
if not split:
|
||||
if not split and not single_partition:
|
||||
assert layout # Initialized above for not single_partition case (mypy needs this)
|
||||
embed_firmware(args, chroot)
|
||||
write_cgpt_kpart(args, layout, chroot)
|
||||
|
||||
|
@ -998,7 +1010,9 @@ def install_system_image(
|
|||
pmb.chroot.user(["mv", "-f", sys_image_patched, sys_image], working_dir=workdir)
|
||||
|
||||
|
||||
def print_flash_info(device: str, deviceinfo: Deviceinfo, split: bool, have_disk: bool) -> None:
|
||||
def print_flash_info(
|
||||
device: str, deviceinfo: Deviceinfo, split: bool, have_disk: bool, single_partition: bool
|
||||
) -> None:
|
||||
"""Print flashing information, based on the deviceinfo data and the
|
||||
pmbootstrap arguments."""
|
||||
logging.info("") # make the note stand out
|
||||
|
@ -1029,6 +1043,7 @@ def print_flash_info(device: str, deviceinfo: Deviceinfo, split: bool, have_disk
|
|||
logging.info(f" {Chroot.native() / 'home/pmos/rootfs' / device}-root.img")
|
||||
else:
|
||||
logging.info(f" {Chroot.native() / 'home/pmos/rootfs' / device}.img")
|
||||
if not single_partition:
|
||||
logging.info(
|
||||
" (NOTE: This file has a partition table, which"
|
||||
" contains /boot and / subpartitions. That way we"
|
||||
|
@ -1187,6 +1202,7 @@ def install_on_device_installer(args: PmbArgs, step: int, steps: int) -> None:
|
|||
boot_label,
|
||||
"pmOS_install",
|
||||
args.split,
|
||||
args.single_partition,
|
||||
args.disk,
|
||||
)
|
||||
|
||||
|
@ -1411,6 +1427,13 @@ def install(args: PmbArgs) -> None:
|
|||
if args.on_device_installer:
|
||||
sanity_check_ondev_version(args)
|
||||
|
||||
# --single-partition implies --no-split. There is nothing to split if
|
||||
# there is only a single partition.
|
||||
if args.single_partition:
|
||||
args.split = False
|
||||
if deviceinfo.create_initfs_extra:
|
||||
raise RuntimeError("--single-partition does not work for devices with initramfs-extra")
|
||||
|
||||
# Number of steps for the different installation methods.
|
||||
if args.no_image:
|
||||
steps = 2
|
||||
|
@ -1444,10 +1467,23 @@ def install(args: PmbArgs) -> None:
|
|||
# Runs install_system_image twice
|
||||
install_on_device_installer(args, step, steps)
|
||||
else:
|
||||
install_system_image(args, 0, chroot, step, steps, split=args.split, disk=args.disk)
|
||||
install_system_image(
|
||||
args,
|
||||
0,
|
||||
chroot,
|
||||
step,
|
||||
steps,
|
||||
split=args.split,
|
||||
disk=args.disk,
|
||||
single_partition=args.single_partition,
|
||||
)
|
||||
|
||||
print_flash_info(
|
||||
device, deviceinfo, args.split, True if args.disk and args.disk.is_absolute() else False
|
||||
device,
|
||||
deviceinfo,
|
||||
args.split,
|
||||
True if args.disk and args.disk.is_absolute() else False,
|
||||
args.single_partition,
|
||||
)
|
||||
print_sshd_info(args)
|
||||
print_firewall_info(args.no_firewall, deviceinfo.arch)
|
||||
|
|
|
@ -205,23 +205,28 @@ def format_and_mount_root(
|
|||
|
||||
def format(
|
||||
args: PmbArgs,
|
||||
layout: PartitionLayout,
|
||||
layout: PartitionLayout | None,
|
||||
boot_label: str,
|
||||
root_label: str,
|
||||
disk: PathString | None,
|
||||
) -> None:
|
||||
"""
|
||||
:param layout: partition layout from get_partition_layout()
|
||||
:param layout: partition layout from get_partition_layout() or None
|
||||
:param boot_label: label of the boot partition (e.g. "pmOS_boot")
|
||||
:param root_label: label of the root partition (e.g. "pmOS_root")
|
||||
:param disk: path to disk block device (e.g. /dev/mmcblk0) or None
|
||||
"""
|
||||
if layout:
|
||||
root_dev = f"/dev/installp{layout['root']}"
|
||||
boot_dev = f"/dev/installp{layout['boot']}"
|
||||
else:
|
||||
root_dev = "/dev/install"
|
||||
boot_dev = None
|
||||
|
||||
if args.full_disk_encryption:
|
||||
format_luks_root(args, root_dev)
|
||||
root_dev = "/dev/mapper/pm_crypt"
|
||||
|
||||
format_and_mount_root(args, root_dev, root_label, disk)
|
||||
if boot_dev:
|
||||
format_and_mount_boot(args, boot_dev, boot_label)
|
||||
|
|
|
@ -120,6 +120,14 @@ def arguments_install(subparser: argparse._SubParsersAction) -> None:
|
|||
choices=[512, 2048, 4096],
|
||||
)
|
||||
|
||||
ret.add_argument(
|
||||
"--single-partition",
|
||||
action="store_true",
|
||||
help="Create a single partition that contains both boot and root files."
|
||||
" This can be used on devices that boot without mounting the boot file"
|
||||
" system (e.g. Android boot images or fastboot).",
|
||||
)
|
||||
|
||||
# Image type
|
||||
group_desc = ret.add_argument_group(
|
||||
"optional image type",
|
||||
|
|
|
@ -118,6 +118,7 @@ class Deviceinfo:
|
|||
# bootloader
|
||||
flash_method: str = ""
|
||||
boot_filesystem: str | None = ""
|
||||
create_initfs_extra: bool | None = False
|
||||
|
||||
# flash
|
||||
flash_heimdall_partition_kernel: str | None = ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue