From 33e1214aef22a2d8c0ac5eedadd9d35bfe732a36 Mon Sep 17 00:00:00 2001 From: InsanePrawn Date: Sun, 30 Apr 2023 03:24:17 +0200 Subject: [PATCH] image/fastboot: add --confirm option and generalize fastboot_erase{_dtbo,}() --- image/boot.py | 33 +++++++++++++++++++++-------- image/fastboot.py | 53 +++++++++++++++++++++++++++++++++-------------- image/flash.py | 17 +++++++++++---- 3 files changed, 75 insertions(+), 28 deletions(-) diff --git a/image/boot.py b/image/boot.py index a24e2e5..73ea6fd 100644 --- a/image/boot.py +++ b/image/boot.py @@ -12,28 +12,42 @@ from flavours.flavour import get_profile_flavour from flavours.cli import profile_option from wrapper import enforce_wrap -from .fastboot import fastboot_boot, fastboot_erase_dtbo +from .fastboot import fastboot_boot, fastboot_erase from .image import get_device_name, losetup_rootfs_image, get_image_path, dump_aboot, dump_lk2nd LK2ND = FLASH_PARTS['LK2ND'] ABOOT = FLASH_PARTS['ABOOT'] -TYPES = [LK2ND, JUMPDRIVE, ABOOT] +BOOT_TYPES = [LK2ND, JUMPDRIVE, ABOOT] @click.command(name='boot') @profile_option -@click.argument('type', required=False, default=ABOOT, type=click.Choice(TYPES)) +@click.argument('type', required=False, default=ABOOT, type=click.Choice(BOOT_TYPES)) @click.option('-b', '--sector-size', type=int, help="Override the device's sector size", default=None) -def cmd_boot(type: str, profile: Optional[str] = None, sector_size: Optional[int] = None): +@click.option( + '--erase-dtbo/--no-erase-dtbo', + is_flag=True, + default=True, + show_default=True, + help="Erase the DTBO partition before flashing", +) +@click.option('--confirm', is_flag=True, help="Ask for confirmation before executing fastboot commands") +def cmd_boot( + type: str, + profile: Optional[str] = None, + sector_size: Optional[int] = None, + erase_dtbo: bool = True, + confirm: bool = False, +): """Boot JumpDrive or the Kupfer aboot image. Erases Android DTBO in the process.""" enforce_wrap() device = get_profile_device(profile) flavour = get_profile_flavour(profile).name deviceinfo = device.parse_deviceinfo() - sector_size = sector_size or deviceinfo.flash_pagesize + sector_size = sector_size or device.get_image_sectorsize_default() if not sector_size: - raise Exception(f"Device {device.name} has no flash_pagesize specified") + raise Exception(f"Device {device.name} has no rootfs_image_sector_size specified") image_path = get_image_path(device, flavour) strategy = deviceinfo.flash_method if not strategy: @@ -54,7 +68,8 @@ def cmd_boot(type: str, profile: Optional[str] = None, sector_size: Optional[int path = dump_aboot(loop_device + 'p1') else: raise Exception(f'Unknown boot image type {type}') - fastboot_erase_dtbo() - fastboot_boot(path) + if erase_dtbo: + fastboot_erase('dtbo', confirm=confirm) + fastboot_boot(path, confirm=confirm) else: - raise Exception(f"Unknown flash strategy {strategy} for device {device.name}") + raise Exception(f'Unsupported flash strategy "{strategy}" for device {device.name}') diff --git a/image/fastboot.py b/image/fastboot.py index 937b348..cbe7ec6 100644 --- a/image/fastboot.py +++ b/image/fastboot.py @@ -1,42 +1,65 @@ +import click import logging from exec.cmd import run_cmd, CompletedProcess from typing import Optional -def fastboot_erase_dtbo(): - logging.info("Fastboot: Erasing DTBO") +def confirm_cmd(cmd: list[str], color='green', default=True, msg='Really execute fastboot cmd?') -> bool: + return click.confirm( + f'{click.style(msg, fg=color, bold=True)} {" ".join(cmd)}', + default=default, + abort=False, + ) + + +def fastboot_erase(target: str, confirm: bool = False): + if not target: + raise Exception(f"No fastboot erase target specified: {repr(target)}") + cmd = [ + 'fastboot', + 'erase', + target, + ] + if confirm: + if not confirm_cmd(cmd, msg=f'Really erase fastboot "{target}" partition?', color='yellow'): + raise Exception("user aborted") + logging.info(f"Fastboot: Erasing {target}") run_cmd( - [ - 'fastboot', - 'erase', - 'dtbo', - ], + cmd, capture_output=True, ) -def fastboot_flash(partition: str, file: str, sparse_size: Optional[str] = None): - logging.info(f"Fastboot: Flashing {file} to {partition}") - result = run_cmd([ +def fastboot_flash(partition: str, file: str, sparse_size: Optional[str] = None, confirm: bool = False): + cmd = [ 'fastboot', *(['-S', sparse_size] if sparse_size is not None else []), 'flash', partition, file, - ]) + ] + if confirm: + if not confirm_cmd(cmd): + raise Exception("user aborted") + logging.info(f"Fastboot: Flashing {file} to {partition}") + result = run_cmd(cmd) assert isinstance(result, CompletedProcess) if result.returncode != 0: raise Exception(f'Failed to flash {file}') -def fastboot_boot(file): - logging.info(f"Fastboot: booting {file}") - result = run_cmd([ +def fastboot_boot(file, confirm: bool = False): + cmd = [ 'fastboot', 'boot', file, - ]) + ] + if confirm: + if not confirm_cmd(cmd): + raise Exception("user aborted") + logging.info(f"Fastboot: booting {file}") + result = run_cmd(cmd) assert isinstance(result, CompletedProcess) if result.returncode != 0: raise Exception(f'Failed to boot {file} using fastboot') diff --git a/image/flash.py b/image/flash.py index afa8f60..da8c9ad 100644 --- a/image/flash.py +++ b/image/flash.py @@ -63,8 +63,9 @@ def prepare_minimal_image(source_path: str, sector_size: int) -> str: @profile_option @click.option('-m', '--method', type=click.Choice(FLASH_METHODS)) @click.option('--split-size', help='Chunk size when splitting the image into sparse files via fastboot') -@click.option('--shrink/--no-shrink', is_flag=True, default=True, help="Don't copy and shrink the image file to minimal size") +@click.option('--shrink/--no-shrink', is_flag=True, default=True, help="Copy and shrink the image file to minimal size") @click.option('-b', '--sector-size', type=int, help="Override the device's sector size", default=None) +@click.option('--confirm', is_flag=True, help="Ask for confirmation before executing fastboot commands") @click.argument('what', type=click.Choice(list(FLASH_PARTS.values()))) @click.argument('location', type=str, required=False) def cmd_flash( @@ -75,6 +76,7 @@ def cmd_flash( profile: Optional[str] = None, shrink: bool = True, sector_size: Optional[int] = None, + confirm: bool = False, ): """Flash a partition onto a device. `location` takes either a path to a block device or one of emmc, sdcard""" enforce_wrap() @@ -91,6 +93,12 @@ def cmd_flash( if what not in FLASH_PARTS.values(): raise Exception(f'Unknown what "{what}", must be one of {", ".join(FLASH_PARTS.values())}') + if location and location.startswith('aboot'): + raise Exception("You're trying to flash something to your aboot partition, " + "which contains the android bootloader itself.\n" + "This will brick your phone and is not what you want.\n" + 'Aborting.\nDid you mean "boot"?') + if what == ROOTFS: path = '' if method not in FLASH_METHODS: @@ -104,6 +112,7 @@ def cmd_flash( partition=location, file=image_path, sparse_size=split_size if split_size is not None else '100M', + confirm=confirm, ) elif method in [JUMPDRIVE, DD]: if method == DD or location.startswith("/") or (location not in LOCATIONS and os.path.exists(location)): @@ -121,12 +130,12 @@ def cmd_flash( loop_device = losetup_rootfs_image(device_image_path, sector_size) if what == ABOOT: path = dump_aboot(f'{loop_device}p1') - fastboot_flash('boot', path) + fastboot_flash(location or 'boot', path, confirm=confirm) elif what == LK2ND: path = dump_lk2nd(f'{loop_device}p1') - fastboot_flash('lk2nd', path) + fastboot_flash(location or 'lk2nd', path, confirm=confirm) elif what == QHYPSTUB: path = dump_qhypstub(f'{loop_device}p1') - fastboot_flash('qhypstub', path) + fastboot_flash(location or 'qhypstub', path, confirm=confirm) else: raise Exception(f'Unknown what "{what}", this must be a bug in kupferbootstrap!')