You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/apps/common/sdcard.py

190 lines
5.6 KiB

from typing import TYPE_CHECKING
from trezor import TR, io, utils, wire
from trezor.ui.layouts import confirm_action, show_error_and_raise
from trezor.utils import sd_hotswap_enabled
if TYPE_CHECKING:
from trezor.ui.layouts.common import ProgressLayout
class SdCardUnavailable(wire.ProcessError):
pass
async def _confirm_retry_wrong_card() -> None:
if sd_hotswap_enabled():
await confirm_action(
"warning_wrong_sd",
TR.sd_card__title,
TR.sd_card__wrong_sd_card,
TR.sd_card__insert_correct_card,
verb=TR.buttons__retry,
verb_cancel=TR.buttons__abort,
exc=SdCardUnavailable("Wrong SD card."),
)
else:
await show_error_and_raise(
"warning_wrong_sd",
TR.sd_card__unplug_and_insert_correct,
TR.sd_card__wrong_sd_card,
exc=SdCardUnavailable("Wrong SD card."),
)
async def _confirm_retry_insert_card() -> None:
if sd_hotswap_enabled():
await confirm_action(
"warning_no_sd",
TR.sd_card__title,
TR.sd_card__card_required,
TR.sd_card__please_insert,
verb=TR.buttons__retry,
verb_cancel=TR.buttons__abort,
exc=SdCardUnavailable("SD card required."),
)
else:
await show_error_and_raise(
"warning_no_sd",
TR.sd_card__please_unplug_and_insert,
TR.sd_card__card_required,
exc=SdCardUnavailable("SD card required."),
)
async def _confirm_format_card() -> None:
# Format card? yes/no
await confirm_action(
"warning_format_sd",
TR.sd_card__error,
TR.sd_card__unknown_filesystem,
TR.sd_card__use_different_card,
verb=TR.buttons__format,
verb_cancel=TR.buttons__cancel,
exc=SdCardUnavailable("SD card not formatted."),
)
# Confirm formatting
await confirm_action(
"confirm_format_sd",
TR.sd_card__format_card,
TR.sd_card__all_data_will_be_lost,
TR.sd_card__wanna_format,
reverse=True,
verb=TR.sd_card__format_card,
hold=True,
exc=SdCardUnavailable("SD card not formatted."),
)
async def confirm_retry_sd(
exc: wire.ProcessError = SdCardUnavailable("Error accessing SD card."),
) -> None:
await confirm_action(
"warning_sd_retry",
TR.sd_card__title_problem,
None,
TR.sd_card__problem_accessing,
verb=TR.buttons__retry,
verb_cancel=TR.buttons__abort,
exc=exc,
)
async def ensure_sdcard(ensure_filesystem: bool = True) -> None:
"""Ensure a SD card is ready for use.
This function runs the UI flow needed to ask the user to insert a SD card if there
isn't one.
If `ensure_filesystem` is True (the default), it also tries to mount the SD card
filesystem, and allows the user to format the card if a filesystem cannot be
mounted.
"""
from trezor import sdcard
while not sdcard.is_present():
await _confirm_retry_insert_card()
if not ensure_filesystem:
return
fatfs = io.fatfs # local_cache_attribute
while True:
try:
try:
with sdcard.filesystem(mounted=False):
fatfs.mount()
except fatfs.NoFilesystem:
# card not formatted. proceed out of the except clause
pass
else:
# no error when mounting
return
await _confirm_format_card()
# Proceed to formatting. Failure is caught by the outside OSError handler
with sdcard.filesystem(mounted=False):
render_func = None
if not utils.DISABLE_ANIMATION:
_start_progress()
render_func = _render_progress
fatfs.mkfs(render_func)
fatfs.mount()
fatfs.setlabel("TREZOR")
if not utils.DISABLE_ANIMATION:
_finish_progress()
# format and mount succeeded
return
except OSError:
# formatting failed, or generic I/O error (SD card power-on failed)
await confirm_retry_sd()
async def request_sd_salt() -> bytearray | None:
import storage.sd_salt as storage_sd_salt
if not storage_sd_salt.is_enabled():
return None
while True:
await ensure_sdcard(ensure_filesystem=False)
try:
return storage_sd_salt.load_sd_salt()
except (storage_sd_salt.WrongSdCard, io.fatfs.NoFilesystem):
await _confirm_retry_wrong_card()
except OSError:
# Generic problem with loading the SD salt (hardware problem, or we could
# not read the file, or there is a staged salt which cannot be committed).
# In either case, there is no good way to recover. If the user clicks Retry,
# we will try again.
await confirm_retry_sd()
_progress_obj: ProgressLayout | None = None
def _start_progress() -> None:
from trezor import workflow
from trezor.ui.layouts.progress import progress
global _progress_obj
# Because we are drawing to the screen manually, without a layout, we
# should make sure that no other layout is running.
workflow.close_others()
_progress_obj = progress()
def _render_progress(progress: int) -> None:
global _progress_obj
if _progress_obj is not None:
_progress_obj.report(progress)
def _finish_progress() -> None:
global _progress_obj
_progress_obj = None