From 18ac4fc9ca4150c746f5a200d12f8e63bf559c12 Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 26 Feb 2020 19:25:22 +0100 Subject: [PATCH] core: update Python facing APIs --- core/src/apps/common/sdcard.py | 23 ++++---- core/src/apps/debug/__init__.py | 3 +- core/src/storage/sd_salt.py | 83 ++++++++++++++--------------- core/src/trezor/__init__.py | 2 + core/src/trezor/sdcard.py | 26 +++++---- core/tests/test_trezor.sdcard.py | 90 +++++++++++++++----------------- 6 files changed, 118 insertions(+), 109 deletions(-) diff --git a/core/src/apps/common/sdcard.py b/core/src/apps/common/sdcard.py index 56a0dd2d4f..8c8984bef9 100644 --- a/core/src/apps/common/sdcard.py +++ b/core/src/apps/common/sdcard.py @@ -1,6 +1,6 @@ import storage.sd_salt from storage.sd_salt import SD_CARD_HOT_SWAPPABLE -from trezor import sdcard, ui, wire +from trezor import fatfs, sdcard, ui, wire from trezor.ui.text import Text from apps.common.confirm import confirm, hold_to_confirm @@ -91,8 +91,8 @@ async def ensure_sdcard( while True: try: - with sdcard.get_filesystem(mounted=False) as fs: - fs.mount() + with sdcard.filesystem(mounted=False): + fatfs.mount() # Mount succeeded, filesystem is OK return except OSError: @@ -103,13 +103,18 @@ async def ensure_sdcard( raise SdCardUnavailable("SD card not formatted.") try: - with sdcard.get_filesystem(mounted=False) as fs: - fs.mkfs() - # mkfs succeeded. Re-run loop to retry mounting. - continue + with sdcard.filesystem(mounted=False): + fatfs.mkfs() + fatfs.mount() + fatfs.setlabel("TREZOR") + # mkfs and mount succeeded + return except OSError: - if not await sd_problem_dialog(ctx): - raise SdCardUnavailable("Problem formatting SD card.") + pass + + # allow retry if we get as far as here + if not await sd_problem_dialog(ctx): + raise SdCardUnavailable("Problem formatting SD card.") async def request_sd_salt( diff --git a/core/src/apps/debug/__init__.py b/core/src/apps/debug/__init__.py index af2122e473..74b22600ec 100644 --- a/core/src/apps/debug/__init__.py +++ b/core/src/apps/debug/__init__.py @@ -145,8 +145,7 @@ if __debug__: try: io.sdcard.power_on() if msg.format: - fs = io.FatFS() - fs.mkfs() + io.fatfs.mkfs() else: # trash first 1 MB of data to destroy the FAT filesystem assert io.sdcard.capacity() >= 1024 * 1024 diff --git a/core/src/storage/sd_salt.py b/core/src/storage/sd_salt.py index 654d57ec02..0c8de52715 100644 --- a/core/src/storage/sd_salt.py +++ b/core/src/storage/sd_salt.py @@ -1,13 +1,13 @@ from micropython import const import storage.device +from trezor import fatfs from trezor.crypto import hmac from trezor.crypto.hashlib import sha256 -from trezor.sdcard import get_filesystem +from trezor.sdcard import with_filesystem from trezor.utils import consteq if False: - from trezor import io from typing import Optional, TypeVar, Callable T = TypeVar("T", bound=Callable) @@ -38,10 +38,11 @@ def _get_salt_path(new: bool = False) -> str: return "{}/salt{}".format(_get_device_dir(), ".new" if new else "") -def _load_salt(fs: io.FatFS, auth_key: bytes, path: str) -> Optional[bytearray]: +@with_filesystem +def _load_salt(auth_key: bytes, path: str) -> Optional[bytearray]: # Load the salt file if it exists. try: - with fs.open(path, "r") as f: + with fatfs.open(path, "r") as f: salt = bytearray(SD_SALT_LEN_BYTES) stored_tag = bytearray(SD_SALT_AUTH_TAG_LEN_BYTES) f.read(salt) @@ -57,6 +58,7 @@ def _load_salt(fs: io.FatFS, auth_key: bytes, path: str) -> Optional[bytearray]: return salt +@with_filesystem def load_sd_salt() -> Optional[bytearray]: salt_auth_key = storage.device.get_sd_salt_auth_key() if salt_auth_key is None: @@ -65,55 +67,54 @@ def load_sd_salt() -> Optional[bytearray]: salt_path = _get_salt_path() new_salt_path = _get_salt_path(new=True) - with get_filesystem() as fs: - salt = _load_salt(fs, salt_auth_key, salt_path) - if salt is not None: - return salt - - # Check if there is a new salt. - salt = _load_salt(fs, salt_auth_key, new_salt_path) - if salt is None: - # No valid salt file on this SD card. - raise WrongSdCard - - # Normal salt file does not exist, but new salt file exists. That means that - # SD salt regeneration was interrupted earlier. Bring into consistent state. - # TODO Possibly overwrite salt file with random data. - try: - fs.unlink(salt_path) - except OSError: - pass - - # fs.rename can fail with a write error, which falls through as an OSError. - # This should be handled in calling code, by allowing the user to retry. - fs.rename(new_salt_path, salt_path) + salt = _load_salt(salt_auth_key, salt_path) + if salt is not None: return salt + # Check if there is a new salt. + salt = _load_salt(salt_auth_key, new_salt_path) + if salt is None: + # No valid salt file on this SD card. + raise WrongSdCard + # Normal salt file does not exist, but new salt file exists. That means that + # SD salt regeneration was interrupted earlier. Bring into consistent state. + # TODO Possibly overwrite salt file with random data. + try: + fatfs.unlink(salt_path) + except OSError: + pass + + # fatfs.rename can fail with a write error, which falls through as an OSError. + # This should be handled in calling code, by allowing the user to retry. + fatfs.rename(new_salt_path, salt_path) + return salt + + +@with_filesystem def set_sd_salt(salt: bytes, salt_tag: bytes, stage: bool = False) -> None: salt_path = _get_salt_path(stage) - with get_filesystem() as fs: - fs.mkdir("/trezor", True) - fs.mkdir(_get_device_dir(), True) - with fs.open(salt_path, "w") as f: - f.write(salt) - f.write(salt_tag) + fatfs.mkdir("/trezor", True) + fatfs.mkdir(_get_device_dir(), True) + with fatfs.open(salt_path, "w") as f: + f.write(salt) + f.write(salt_tag) +@with_filesystem def commit_sd_salt() -> None: salt_path = _get_salt_path(new=False) new_salt_path = _get_salt_path(new=True) - with get_filesystem() as fs: - try: - fs.unlink(salt_path) - except OSError: - pass - fs.rename(new_salt_path, salt_path) + try: + fatfs.unlink(salt_path) + except OSError: + pass + fatfs.rename(new_salt_path, salt_path) +@with_filesystem def remove_sd_salt() -> None: salt_path = _get_salt_path() - with get_filesystem() as fs: - # TODO Possibly overwrite salt file with random data. - fs.unlink(salt_path) + # TODO Possibly overwrite salt file with random data. + fatfs.unlink(salt_path) diff --git a/core/src/trezor/__init__.py b/core/src/trezor/__init__.py index 80c7ec8153..25c858cdee 100644 --- a/core/src/trezor/__init__.py +++ b/core/src/trezor/__init__.py @@ -1,2 +1,4 @@ import trezorconfig as config # noqa: F401 import trezorio as io # noqa: F401 + +fatfs = io.fatfs diff --git a/core/src/trezor/sdcard.py b/core/src/trezor/sdcard.py index 0f8d340d85..06c6c39e99 100644 --- a/core/src/trezor/sdcard.py +++ b/core/src/trezor/sdcard.py @@ -1,14 +1,15 @@ -from trezorio import FatFS, sdcard +from trezorio import fatfs, sdcard if False: - from typing import Any, Optional + from typing import Any, Callable, Optional, TypeVar + + T = TypeVar("T", bound=Callable) class FilesystemWrapper: _INSTANCE = None # type: Optional[FilesystemWrapper] def __init__(self, mounted: bool) -> None: - self.fs = FatFS() self.mounted = mounted self.counter = 0 @@ -21,20 +22,18 @@ class FilesystemWrapper: return cls._INSTANCE def _deinit_instance(self) -> None: - if self.mounted: - self.fs.unmount() + fatfs.unmount() sdcard.power_off() FilesystemWrapper._INSTANCE = None - def __enter__(self) -> "FatFS": + def __enter__(self) -> None: try: if self.counter <= 0: self.counter = 0 sdcard.power_on() if self.mounted: - self.fs.mount() + fatfs.mount() self.counter += 1 - return self.fs except Exception: self._deinit_instance() raise @@ -46,8 +45,17 @@ class FilesystemWrapper: self._deinit_instance() -def get_filesystem(mounted: bool = True) -> FilesystemWrapper: +def filesystem(mounted: bool = True) -> FilesystemWrapper: return FilesystemWrapper.get_instance(mounted=mounted) +def with_filesystem(func: T) -> T: + def wrapped_func(*args, **kwargs) -> Any: # type: ignore + with filesystem(): + return func(*args, **kwargs) + + return wrapped_func # type: ignore + + is_present = sdcard.is_present +capacity = sdcard.capacity diff --git a/core/tests/test_trezor.sdcard.py b/core/tests/test_trezor.sdcard.py index 98707a38c0..f70853ac5b 100644 --- a/core/tests/test_trezor.sdcard.py +++ b/core/tests/test_trezor.sdcard.py @@ -1,75 +1,68 @@ from common import * -from trezor import io, sdcard +from trezor import io, fatfs, sdcard class TestTrezorSdcard(unittest.TestCase): def test_power(self): - # io.sdcard.capacity() will return 0 if the card is not powered, + # sdcard.capacity() will return 0 if the card is not powered, # non-zero value otherwise - self.assertEqual(io.sdcard.capacity(), 0) - with sdcard.get_filesystem(mounted=False): - self.assertTrue(io.sdcard.capacity() > 0) - self.assertEqual(io.sdcard.capacity(), 0) + self.assertEqual(sdcard.capacity(), 0) + with sdcard.filesystem(mounted=False): + self.assertTrue(sdcard.capacity() > 0) + self.assertEqual(sdcard.capacity(), 0) def test_nomount(self): - with sdcard.get_filesystem(mounted=False) as fs: - with self.assertRaises(OSError): - fs.listdir("/") + with sdcard.filesystem(mounted=False): + self.assertFalse(fatfs.is_mounted()) def test_mount(self): # set up a filesystem first - with sdcard.get_filesystem(mounted=False) as fs: - fs.mkfs() + with sdcard.filesystem(mounted=False): + fatfs.mkfs() - with sdcard.get_filesystem() as fs: - # the following should succeed - fs.listdir("/") + with sdcard.filesystem(): + self.assertTrue(fatfs.is_mounted()) - # filesystem should not be available - with self.assertRaises(OSError): - fs.listdir("/") + self.assertFalse(fatfs.is_mounted()) def test_nesting(self): # set up a filesystem first - with sdcard.get_filesystem(mounted=False) as fs: - fs.mkfs() + with sdcard.filesystem(mounted=False): + fatfs.mkfs() - self.assertEqual(io.sdcard.capacity(), 0) - with sdcard.get_filesystem() as fs_a: - self.assertTrue(io.sdcard.capacity() > 0) - with sdcard.get_filesystem() as fs_b: - self.assertTrue(io.sdcard.capacity() > 0) - self.assertIs(fs_a, fs_b) - fs_b.listdir("/") - self.assertTrue(io.sdcard.capacity() > 0) - # filesystem should still be mounted - fs_a.listdir("/") + self.assertEqual(sdcard.capacity(), 0) + with sdcard.filesystem(): + self.assertTrue(sdcard.capacity() > 0) + self.assertTrue(fatfs.is_mounted()) + with sdcard.filesystem(): + self.assertTrue(sdcard.capacity() > 0) + self.assertTrue(fatfs.is_mounted()) - self.assertEqual(io.sdcard.capacity(), 0) - # filesystem should not be available - with self.assertRaises(OSError): - fs_a.listdir("/") + self.assertTrue(sdcard.capacity() > 0) + self.assertTrue(fatfs.is_mounted()) + + self.assertEqual(sdcard.capacity(), 0) + self.assertFalse(fatfs.is_mounted()) def test_mount_nomount(self): with self.assertRaises(RuntimeError): - with sdcard.get_filesystem(mounted=True): - with sdcard.get_filesystem(mounted=False): + with sdcard.filesystem(mounted=True): + with sdcard.filesystem(mounted=False): pass with self.assertRaises(RuntimeError): - with sdcard.get_filesystem(mounted=False): - with sdcard.get_filesystem(mounted=True): + with sdcard.filesystem(mounted=False): + with sdcard.filesystem(mounted=True): pass def test_failed_mount(self): # set up a filesystem first - with sdcard.get_filesystem(mounted=False) as fs: - fs.mkfs() + with sdcard.filesystem(mounted=False): + fatfs.mkfs() - with sdcard.get_filesystem() as fs: - # the following should succeed - fs.listdir("/") + with sdcard.filesystem(): + self.assertTrue(fatfs.is_mounted()) # trash filesystem io.sdcard.power_on() @@ -78,17 +71,18 @@ class TestTrezorSdcard(unittest.TestCase): # mounting should now fail with self.assertRaises(OSError): - with sdcard.get_filesystem() as fs: + with sdcard.filesystem(): pass + self.assertFalse(fatfs.is_mounted()) + # it should be possible to create an unmounted instance - with sdcard.get_filesystem(mounted=False) as fs: - fs.mkfs() + with sdcard.filesystem(mounted=False): + fatfs.mkfs() # mounting should now succeed - with sdcard.get_filesystem() as fs: - fs.listdir("/") - + with sdcard.filesystem(): + self.assertTrue(fatfs.is_mounted()) if __name__ == "__main__":