From e9c275c24f361579f3b06fd99252e92ecb3581e7 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 21 Feb 2020 15:53:16 +0100 Subject: [PATCH] core/sdcard: fix invalid state when filesystem mounting fails --- core/src/trezor/sdcard.py | 31 +++++++++++++++++++------------ core/tests/test_trezor.sdcard.py | 27 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/core/src/trezor/sdcard.py b/core/src/trezor/sdcard.py index de4fbf8467..0f8d340d85 100644 --- a/core/src/trezor/sdcard.py +++ b/core/src/trezor/sdcard.py @@ -7,7 +7,7 @@ if False: class FilesystemWrapper: _INSTANCE = None # type: Optional[FilesystemWrapper] - def __init__(self, mounted: bool = True) -> None: + def __init__(self, mounted: bool) -> None: self.fs = FatFS() self.mounted = mounted self.counter = 0 @@ -20,23 +20,30 @@ class FilesystemWrapper: raise RuntimeError # cannot request mounted and non-mounted instance at the same time return cls._INSTANCE + def _deinit_instance(self) -> None: + if self.mounted: + self.fs.unmount() + sdcard.power_off() + FilesystemWrapper._INSTANCE = None + def __enter__(self) -> "FatFS": - if self.counter <= 0: - self.counter = 0 - sdcard.power_on() - if self.mounted: - self.fs.mount() - self.counter += 1 - return self.fs + try: + if self.counter <= 0: + self.counter = 0 + sdcard.power_on() + if self.mounted: + self.fs.mount() + self.counter += 1 + return self.fs + except Exception: + self._deinit_instance() + raise def __exit__(self, exc_type: Any, exc_val: Any, tb: Any) -> None: self.counter -= 1 if self.counter <= 0: self.counter = 0 - if self.mounted: - self.fs.unmount() - sdcard.power_off() - FilesystemWrapper._INSTANCE = None + self._deinit_instance() def get_filesystem(mounted: bool = True) -> FilesystemWrapper: diff --git a/core/tests/test_trezor.sdcard.py b/core/tests/test_trezor.sdcard.py index 66cb304fb3..98707a38c0 100644 --- a/core/tests/test_trezor.sdcard.py +++ b/core/tests/test_trezor.sdcard.py @@ -62,6 +62,33 @@ class TestTrezorSdcard(unittest.TestCase): with sdcard.get_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.get_filesystem() as fs: + # the following should succeed + fs.listdir("/") + + # trash filesystem + io.sdcard.power_on() + io.sdcard.write(0, bytes([0xFF] * io.sdcard.BLOCK_SIZE)) + io.sdcard.power_off() + + # mounting should now fail + with self.assertRaises(OSError): + with sdcard.get_filesystem() as fs: + pass + + # it should be possible to create an unmounted instance + with sdcard.get_filesystem(mounted=False) as fs: + fs.mkfs() + + # mounting should now succeed + with sdcard.get_filesystem() as fs: + fs.listdir("/") + if __name__ == "__main__":