Merge pull request #878 from trezor/matejcik/fatfs-corruption

FatFS API rework
release/2020-03
Tomas Susanka 4 years ago committed by GitHub
commit 86b010a18b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -27,7 +27,19 @@
#include "sdcard.h" #include "sdcard.h"
// clang-format on // clang-format on
/// package: trezorio.__init__ /// package: trezorio.fatfs
static FATFS fs_instance;
bool _fatfs_instance_is_mounted() { return fs_instance.fs_type != 0; }
void _fatfs_unmount_instance() { fs_instance.fs_type = 0; }
#define FATFS_ONLY_MOUNTED \
{ \
if (!_fatfs_instance_is_mounted()) { \
mp_raise_OSError(MP_ENODEV); \
} \
}
DSTATUS disk_initialize(BYTE pdrv) { return disk_status(pdrv); } DSTATUS disk_initialize(BYTE pdrv) { return disk_status(pdrv); }
@ -315,33 +327,14 @@ STATIC const mp_obj_type_t mod_trezorio_FatFSDir_type = {
.iternext = mod_trezorio_FatFSDir_iternext, .iternext = mod_trezorio_FatFSDir_iternext,
}; };
/// class FatFS: /// mock:global
/// """
/// Class encapsulating FAT filesystem
/// """
typedef struct _mp_obj_FatFS_t {
mp_obj_base_t base;
FATFS fs;
} mp_obj_FatFS_t;
/// def __init__(self) -> None: /// def open(path: str, flags: str) -> FatFSFile:
/// """
/// """
STATIC mp_obj_t mod_trezorio_FatFS_make_new(const mp_obj_type_t *type,
size_t n_args, size_t n_kw,
const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 0, false);
mp_obj_FatFS_t *o = m_new_obj(mp_obj_FatFS_t);
o->base.type = type;
return MP_OBJ_FROM_PTR(o);
}
/// def open(self, path: str, flags: str) -> FatFSFile:
/// """ /// """
/// Open or create a file /// Open or create a file
/// """ /// """
STATIC mp_obj_t mod_trezorio_FatFS_open(mp_obj_t self, mp_obj_t path, STATIC mp_obj_t mod_trezorio_fatfs_open(mp_obj_t path, mp_obj_t flags) {
mp_obj_t flags) { FATFS_ONLY_MOUNTED;
mp_buffer_info_t _path, _flags; mp_buffer_info_t _path, _flags;
mp_get_buffer_raise(path, &_path, MP_BUFFER_READ); mp_get_buffer_raise(path, &_path, MP_BUFFER_READ);
mp_get_buffer_raise(flags, &_flags, MP_BUFFER_READ); mp_get_buffer_raise(flags, &_flags, MP_BUFFER_READ);
@ -376,14 +369,15 @@ STATIC mp_obj_t mod_trezorio_FatFS_open(mp_obj_t self, mp_obj_t path,
f->fp = fp; f->fp = fp;
return f; return f;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorio_FatFS_open_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_fatfs_open_obj,
mod_trezorio_FatFS_open); mod_trezorio_fatfs_open);
/// def listdir(self, path: str) -> FatFSDir: /// def listdir(path: str) -> FatFSDir:
/// """ /// """
/// List a directory (return generator) /// List a directory (return generator)
/// """ /// """
STATIC mp_obj_t mod_trezorio_FatFS_listdir(mp_obj_t self, mp_obj_t path) { STATIC mp_obj_t mod_trezorio_fatfs_listdir(mp_obj_t path) {
FATFS_ONLY_MOUNTED;
mp_buffer_info_t _path; mp_buffer_info_t _path;
mp_get_buffer_raise(path, &_path, MP_BUFFER_READ); mp_get_buffer_raise(path, &_path, MP_BUFFER_READ);
DIR dp; DIR dp;
@ -396,19 +390,20 @@ STATIC mp_obj_t mod_trezorio_FatFS_listdir(mp_obj_t self, mp_obj_t path) {
d->dp = dp; d->dp = dp;
return d; return d;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_FatFS_listdir_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_fatfs_listdir_obj,
mod_trezorio_FatFS_listdir); mod_trezorio_fatfs_listdir);
/// def mkdir(self, path: str, exist_ok: bool=False) -> None: /// def mkdir(path: str, exist_ok: bool=False) -> None:
/// """ /// """
/// Create a sub directory /// Create a sub directory
/// """ /// """
STATIC mp_obj_t mod_trezorio_FatFS_mkdir(size_t n_args, const mp_obj_t *args) { STATIC mp_obj_t mod_trezorio_fatfs_mkdir(size_t n_args, const mp_obj_t *args) {
FATFS_ONLY_MOUNTED;
mp_buffer_info_t path; mp_buffer_info_t path;
mp_get_buffer_raise(args[1], &path, MP_BUFFER_READ); mp_get_buffer_raise(args[0], &path, MP_BUFFER_READ);
FRESULT res = f_mkdir(path.buf); FRESULT res = f_mkdir(path.buf);
// directory exists and exist_ok is True, return without failure // directory exists and exist_ok is True, return without failure
if (res == FR_EXIST && n_args > 2 && args[2] == mp_const_true) { if (res == FR_EXIST && n_args > 1 && args[1] == mp_const_true) {
return mp_const_none; return mp_const_none;
} }
if (res != FR_OK) { if (res != FR_OK) {
@ -416,14 +411,15 @@ STATIC mp_obj_t mod_trezorio_FatFS_mkdir(size_t n_args, const mp_obj_t *args) {
} }
return mp_const_none; return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorio_FatFS_mkdir_obj, 2, 3, STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorio_fatfs_mkdir_obj, 1, 2,
mod_trezorio_FatFS_mkdir); mod_trezorio_fatfs_mkdir);
/// def unlink(self, path: str) -> None: /// def unlink(path: str) -> None:
/// """ /// """
/// Delete an existing file or directory /// Delete an existing file or directory
/// """ /// """
STATIC mp_obj_t mod_trezorio_FatFS_unlink(mp_obj_t self, mp_obj_t path) { STATIC mp_obj_t mod_trezorio_fatfs_unlink(mp_obj_t path) {
FATFS_ONLY_MOUNTED;
mp_buffer_info_t _path; mp_buffer_info_t _path;
mp_get_buffer_raise(path, &_path, MP_BUFFER_READ); mp_get_buffer_raise(path, &_path, MP_BUFFER_READ);
FRESULT res = f_unlink(_path.buf); FRESULT res = f_unlink(_path.buf);
@ -432,14 +428,15 @@ STATIC mp_obj_t mod_trezorio_FatFS_unlink(mp_obj_t self, mp_obj_t path) {
} }
return mp_const_none; return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_FatFS_unlink_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_fatfs_unlink_obj,
mod_trezorio_FatFS_unlink); mod_trezorio_fatfs_unlink);
/// def stat(self, path: str) -> Tuple[int, str, str]: /// def stat(path: str) -> Tuple[int, str, str]:
/// """ /// """
/// Get file status /// Get file status
/// """ /// """
STATIC mp_obj_t mod_trezorio_FatFS_stat(mp_obj_t self, mp_obj_t path) { STATIC mp_obj_t mod_trezorio_fatfs_stat(mp_obj_t path) {
FATFS_ONLY_MOUNTED;
mp_buffer_info_t _path; mp_buffer_info_t _path;
mp_get_buffer_raise(path, &_path, MP_BUFFER_READ); mp_get_buffer_raise(path, &_path, MP_BUFFER_READ);
FILINFO info; FILINFO info;
@ -449,15 +446,15 @@ STATIC mp_obj_t mod_trezorio_FatFS_stat(mp_obj_t self, mp_obj_t path) {
} }
return filinfo_to_tuple(&info); return filinfo_to_tuple(&info);
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_FatFS_stat_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_fatfs_stat_obj,
mod_trezorio_FatFS_stat); mod_trezorio_fatfs_stat);
/// def rename(self, oldpath: str, newpath: str) -> None: /// def rename(oldpath: str, newpath: str) -> None:
/// """ /// """
/// Rename/Move a file or directory /// Rename/Move a file or directory
/// """ /// """
STATIC mp_obj_t mod_trezorio_FatFS_rename(mp_obj_t self, mp_obj_t oldpath, STATIC mp_obj_t mod_trezorio_fatfs_rename(mp_obj_t oldpath, mp_obj_t newpath) {
mp_obj_t newpath) { FATFS_ONLY_MOUNTED;
mp_buffer_info_t _oldpath, _newpath; mp_buffer_info_t _oldpath, _newpath;
mp_get_buffer_raise(oldpath, &_oldpath, MP_BUFFER_READ); mp_get_buffer_raise(oldpath, &_oldpath, MP_BUFFER_READ);
mp_get_buffer_raise(newpath, &_newpath, MP_BUFFER_READ); mp_get_buffer_raise(newpath, &_newpath, MP_BUFFER_READ);
@ -467,44 +464,52 @@ STATIC mp_obj_t mod_trezorio_FatFS_rename(mp_obj_t self, mp_obj_t oldpath,
} }
return mp_const_none; return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorio_FatFS_rename_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_fatfs_rename_obj,
mod_trezorio_FatFS_rename); mod_trezorio_fatfs_rename);
/// def mount(self) -> None: /// def mount() -> None:
/// """ /// """
/// Mount/Unmount a logical drive /// Mount the SD card filesystem.
/// """ /// """
STATIC mp_obj_t mod_trezorio_FatFS_mount(mp_obj_t self) { STATIC mp_obj_t mod_trezorio_fatfs_mount() {
mp_obj_FatFS_t *o = MP_OBJ_TO_PTR(self); FRESULT res = f_mount(&fs_instance, "", 1);
FRESULT res = f_mount(&(o->fs), "", 1);
if (res != FR_OK) { if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]); mp_raise_OSError(fresult_to_errno_table[res]);
} }
return mp_const_none; return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_FatFS_mount_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_fatfs_mount_obj,
mod_trezorio_FatFS_mount); mod_trezorio_fatfs_mount);
/// def unmount(self) -> None: /// def unmount() -> None:
/// """ /// """
/// Unmount a logical drive /// Unmount the SD card filesystem.
/// """ /// """
STATIC mp_obj_t mod_trezorio_FatFS_unmount(mp_obj_t self) { STATIC mp_obj_t mod_trezorio_fatfs_unmount() {
// to unmount we have to call mount with the first parameter NULL _fatfs_unmount_instance();
FRESULT res = f_mount(NULL, "", 0);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
return mp_const_none; return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_FatFS_unmount_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_fatfs_unmount_obj,
mod_trezorio_FatFS_unmount); mod_trezorio_fatfs_unmount);
/// def is_mounted() -> bool:
/// """
/// Check if the filesystem is mounted.
/// """
STATIC mp_obj_t mod_trezorio_fatfs_is_mounted() {
return mp_obj_new_bool(_fatfs_instance_is_mounted());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_fatfs_is_mounted_obj,
mod_trezorio_fatfs_is_mounted);
/// def mkfs(self) -> None: /// def mkfs() -> None:
/// """ /// """
/// Create a FAT volume /// Create a FAT volume on the SD card,
/// """ /// """
STATIC mp_obj_t mod_trezorio_FatFS_mkfs(mp_obj_t self) { STATIC mp_obj_t mod_trezorio_fatfs_mkfs() {
if (_fatfs_instance_is_mounted()) {
mp_raise_OSError(MP_EBUSY);
}
MKFS_PARM params = {FM_FAT32, 0, 0, 0, 0}; MKFS_PARM params = {FM_FAT32, 0, 0, 0, 0};
uint8_t working_buf[FF_MAX_SS]; uint8_t working_buf[FF_MAX_SS];
FRESULT res = f_mkfs("", &params, working_buf, sizeof(working_buf)); FRESULT res = f_mkfs("", &params, working_buf, sizeof(working_buf));
@ -513,14 +518,18 @@ STATIC mp_obj_t mod_trezorio_FatFS_mkfs(mp_obj_t self) {
} }
return mp_const_none; return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_FatFS_mkfs_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_fatfs_mkfs_obj,
mod_trezorio_FatFS_mkfs); mod_trezorio_fatfs_mkfs);
/// def setlabel(self, label: str) -> None: /// def setlabel(label: str) -> None:
/// """ /// """
/// Set volume label /// Set volume label
/// """ /// """
STATIC mp_obj_t mod_trezorio_FatFS_setlabel(mp_obj_t self, mp_obj_t label) { STATIC mp_obj_t mod_trezorio_fatfs_setlabel(mp_obj_t label) {
/* setlabel is marked as only-mounted, because "mounting" in ff.c terms means
having parsed the FAT table, which is of course a prerequisite for setting
label. */
FATFS_ONLY_MOUNTED;
mp_buffer_info_t _label; mp_buffer_info_t _label;
mp_get_buffer_raise(label, &_label, MP_BUFFER_READ); mp_get_buffer_raise(label, &_label, MP_BUFFER_READ);
FRESULT res = f_setlabel(_label.buf); FRESULT res = f_setlabel(_label.buf);
@ -529,28 +538,32 @@ STATIC mp_obj_t mod_trezorio_FatFS_setlabel(mp_obj_t self, mp_obj_t label) {
} }
return mp_const_none; return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_FatFS_setlabel_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_fatfs_setlabel_obj,
mod_trezorio_FatFS_setlabel); mod_trezorio_fatfs_setlabel);
STATIC const mp_rom_map_elem_t mod_trezorio_FatFS_locals_dict_table[] = { STATIC const mp_rom_map_elem_t mod_trezorio_fatfs_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mod_trezorio_FatFS_open_obj)}, {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_fatfs)},
{MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mod_trezorio_FatFS_listdir_obj)}, {MP_ROM_QSTR(MP_QSTR_FatFSFile), MP_ROM_PTR(&mod_trezorio_FatFSFile_type)},
{MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mod_trezorio_FatFS_mkdir_obj)}, {MP_ROM_QSTR(MP_QSTR_FatFSDir), MP_ROM_PTR(&mod_trezorio_FatFSDir_type)},
{MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mod_trezorio_FatFS_unlink_obj)},
{MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mod_trezorio_FatFS_rename_obj)}, {MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mod_trezorio_fatfs_open_obj)},
{MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mod_trezorio_FatFS_stat_obj)}, {MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mod_trezorio_fatfs_listdir_obj)},
{MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mod_trezorio_FatFS_mount_obj)}, {MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mod_trezorio_fatfs_mkdir_obj)},
{MP_ROM_QSTR(MP_QSTR_unmount), MP_ROM_PTR(&mod_trezorio_FatFS_unmount_obj)}, {MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mod_trezorio_fatfs_unlink_obj)},
{MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&mod_trezorio_FatFS_mkfs_obj)}, {MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mod_trezorio_fatfs_rename_obj)},
{MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mod_trezorio_fatfs_stat_obj)},
{MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mod_trezorio_fatfs_mount_obj)},
{MP_ROM_QSTR(MP_QSTR_unmount), MP_ROM_PTR(&mod_trezorio_fatfs_unmount_obj)},
{MP_ROM_QSTR(MP_QSTR_is_mounted),
MP_ROM_PTR(&mod_trezorio_fatfs_is_mounted_obj)},
{MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&mod_trezorio_fatfs_mkfs_obj)},
{MP_ROM_QSTR(MP_QSTR_setlabel), {MP_ROM_QSTR(MP_QSTR_setlabel),
MP_ROM_PTR(&mod_trezorio_FatFS_setlabel_obj)}, MP_ROM_PTR(&mod_trezorio_fatfs_setlabel_obj)},
}; };
STATIC MP_DEFINE_CONST_DICT(mod_trezorio_FatFS_locals_dict, STATIC MP_DEFINE_CONST_DICT(mod_trezorio_fatfs_globals,
mod_trezorio_FatFS_locals_dict_table); mod_trezorio_fatfs_globals_table);
STATIC const mp_obj_type_t mod_trezorio_FatFS_type = { STATIC const mp_obj_module_t mod_trezorio_fatfs_module = {
{&mp_type_type}, .base = {&mp_type_module},
.name = MP_QSTR_FatFS, .globals = (mp_obj_dict_t *)&mod_trezorio_fatfs_globals,
.make_new = mod_trezorio_FatFS_make_new,
.locals_dict = (void *)&mod_trezorio_FatFS_locals_dict,
}; };

@ -56,6 +56,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_sdcard_power_on_obj,
/// Power off the SD card interface. /// Power off the SD card interface.
/// """ /// """
STATIC mp_obj_t mod_trezorio_sdcard_power_off() { STATIC mp_obj_t mod_trezorio_sdcard_power_off() {
/* XXX should this call happen inside sdcard_power_off()? */
_fatfs_unmount_instance();
sdcard_power_off(); sdcard_power_off();
return mp_const_none; return mp_const_none;
} }
@ -111,6 +113,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_sdcard_write_obj,
mod_trezorio_sdcard_write); mod_trezorio_sdcard_write);
STATIC const mp_rom_map_elem_t mod_trezorio_sdcard_globals_table[] = { STATIC const mp_rom_map_elem_t mod_trezorio_sdcard_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sdcard)},
{MP_ROM_QSTR(MP_QSTR_is_present), {MP_ROM_QSTR(MP_QSTR_is_present),
MP_ROM_PTR(&mod_trezorio_sdcard_is_present_obj)}, MP_ROM_PTR(&mod_trezorio_sdcard_is_present_obj)},
{MP_ROM_QSTR(MP_QSTR_power_on), {MP_ROM_QSTR(MP_QSTR_power_on),

@ -62,7 +62,7 @@
STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = { STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorio)}, {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorio)},
{MP_ROM_QSTR(MP_QSTR_FatFS), MP_ROM_PTR(&mod_trezorio_FatFS_type)}, {MP_ROM_QSTR(MP_QSTR_fatfs), MP_ROM_PTR(&mod_trezorio_fatfs_module)},
{MP_ROM_QSTR(MP_QSTR_FlashOTP), MP_ROM_PTR(&mod_trezorio_FlashOTP_type)}, {MP_ROM_QSTR(MP_QSTR_FlashOTP), MP_ROM_PTR(&mod_trezorio_FlashOTP_type)},

@ -1,131 +1,6 @@
from typing import * from typing import *
# extmod/modtrezorio/modtrezorio-fatfs.h
class FatFSFile:
"""
Class encapsulating file
"""
def __enter__(self) -> FatFSFile:
"""
Return an open file object
"""
from types import TracebackType
def __exit__(
self, type: Optional[Type[BaseException]],
value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> None:
"""
Close an open file object
"""
def close(self) -> None:
"""
Close an open file object
"""
def read(self, data: bytearray) -> int:
"""
Read data from the file
"""
def write(self, data: Union[bytes, bytearray]) -> int:
"""
Write data to the file
"""
def seek(self, offset: int) -> None:
"""
Move file pointer of the file object
"""
def truncate(self) -> None:
"""
Truncate the file
"""
def sync(self) -> None:
"""
Flush cached data of the writing file
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
class FatFSDir(Iterator[Tuple[int, str, str]]):
"""
Class encapsulating directory
"""
def __next__(self) -> Tuple[int, str, str]:
"""
Read an entry in the directory
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
class FatFS:
"""
Class encapsulating FAT filesystem
"""
def __init__(self) -> None:
"""
"""
def open(self, path: str, flags: str) -> FatFSFile:
"""
Open or create a file
"""
def listdir(self, path: str) -> FatFSDir:
"""
List a directory (return generator)
"""
def mkdir(self, path: str, exist_ok: bool=False) -> None:
"""
Create a sub directory
"""
def unlink(self, path: str) -> None:
"""
Delete an existing file or directory
"""
def stat(self, path: str) -> Tuple[int, str, str]:
"""
Get file status
"""
def rename(self, oldpath: str, newpath: str) -> None:
"""
Rename/Move a file or directory
"""
def mount(self) -> None:
"""
Mount/Unmount a logical drive
"""
def unmount(self) -> None:
"""
Unmount a logical drive
"""
def mkfs(self) -> None:
"""
Create a FAT volume
"""
def setlabel(self, label: str) -> None:
"""
Set volume label
"""
# extmod/modtrezorio/modtrezorio-flash.h # extmod/modtrezorio/modtrezorio-flash.h
class FlashOTP: class FlashOTP:
""" """

@ -0,0 +1,142 @@
from typing import *
# extmod/modtrezorio/modtrezorio-fatfs.h
class FatFSFile:
"""
Class encapsulating file
"""
def __enter__(self) -> FatFSFile:
"""
Return an open file object
"""
from types import TracebackType
def __exit__(
self, type: Optional[Type[BaseException]],
value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> None:
"""
Close an open file object
"""
def close(self) -> None:
"""
Close an open file object
"""
def read(self, data: bytearray) -> int:
"""
Read data from the file
"""
def write(self, data: Union[bytes, bytearray]) -> int:
"""
Write data to the file
"""
def seek(self, offset: int) -> None:
"""
Move file pointer of the file object
"""
def truncate(self) -> None:
"""
Truncate the file
"""
def sync(self) -> None:
"""
Flush cached data of the writing file
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
class FatFSDir(Iterator[Tuple[int, str, str]]):
"""
Class encapsulating directory
"""
def __next__(self) -> Tuple[int, str, str]:
"""
Read an entry in the directory
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def open(path: str, flags: str) -> FatFSFile:
"""
Open or create a file
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def listdir(path: str) -> FatFSDir:
"""
List a directory (return generator)
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def mkdir(path: str, exist_ok: bool=False) -> None:
"""
Create a sub directory
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def unlink(path: str) -> None:
"""
Delete an existing file or directory
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def stat(path: str) -> Tuple[int, str, str]:
"""
Get file status
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def rename(oldpath: str, newpath: str) -> None:
"""
Rename/Move a file or directory
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def mount() -> None:
"""
Mount the SD card filesystem.
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def unmount() -> None:
"""
Unmount the SD card filesystem.
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def is_mounted() -> bool:
"""
Check if the filesystem is mounted.
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def mkfs() -> None:
"""
Create a FAT volume on the SD card,
"""
# extmod/modtrezorio/modtrezorio-fatfs.h
def setlabel(label: str) -> None:
"""
Set volume label
"""

@ -1,6 +1,6 @@
import storage.sd_salt import storage.sd_salt
from storage.sd_salt import SD_CARD_HOT_SWAPPABLE 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 trezor.ui.text import Text
from apps.common.confirm import confirm, hold_to_confirm from apps.common.confirm import confirm, hold_to_confirm
@ -91,8 +91,8 @@ async def ensure_sdcard(
while True: while True:
try: try:
with sdcard.get_filesystem(mounted=False) as fs: with sdcard.filesystem(mounted=False):
fs.mount() fatfs.mount()
# Mount succeeded, filesystem is OK # Mount succeeded, filesystem is OK
return return
except OSError: except OSError:
@ -103,13 +103,18 @@ async def ensure_sdcard(
raise SdCardUnavailable("SD card not formatted.") raise SdCardUnavailable("SD card not formatted.")
try: try:
with sdcard.get_filesystem(mounted=False) as fs: with sdcard.filesystem(mounted=False):
fs.mkfs() fatfs.mkfs()
# mkfs succeeded. Re-run loop to retry mounting. fatfs.mount()
continue fatfs.setlabel("TREZOR")
# mkfs and mount succeeded
return
except OSError: except OSError:
if not await sd_problem_dialog(ctx): pass
raise SdCardUnavailable("Problem formatting SD card.")
# 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( async def request_sd_salt(

@ -145,8 +145,7 @@ if __debug__:
try: try:
io.sdcard.power_on() io.sdcard.power_on()
if msg.format: if msg.format:
fs = io.FatFS() io.fatfs.mkfs()
fs.mkfs()
else: else:
# trash first 1 MB of data to destroy the FAT filesystem # trash first 1 MB of data to destroy the FAT filesystem
assert io.sdcard.capacity() >= 1024 * 1024 assert io.sdcard.capacity() >= 1024 * 1024

@ -1,13 +1,13 @@
from micropython import const from micropython import const
import storage.device import storage.device
from trezor import fatfs
from trezor.crypto import hmac from trezor.crypto import hmac
from trezor.crypto.hashlib import sha256 from trezor.crypto.hashlib import sha256
from trezor.sdcard import get_filesystem from trezor.sdcard import with_filesystem
from trezor.utils import consteq from trezor.utils import consteq
if False: if False:
from trezor import io
from typing import Optional, TypeVar, Callable from typing import Optional, TypeVar, Callable
T = TypeVar("T", bound=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 "") 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. # Load the salt file if it exists.
try: try:
with fs.open(path, "r") as f: with fatfs.open(path, "r") as f:
salt = bytearray(SD_SALT_LEN_BYTES) salt = bytearray(SD_SALT_LEN_BYTES)
stored_tag = bytearray(SD_SALT_AUTH_TAG_LEN_BYTES) stored_tag = bytearray(SD_SALT_AUTH_TAG_LEN_BYTES)
f.read(salt) f.read(salt)
@ -57,6 +58,7 @@ def _load_salt(fs: io.FatFS, auth_key: bytes, path: str) -> Optional[bytearray]:
return salt return salt
@with_filesystem
def load_sd_salt() -> Optional[bytearray]: def load_sd_salt() -> Optional[bytearray]:
salt_auth_key = storage.device.get_sd_salt_auth_key() salt_auth_key = storage.device.get_sd_salt_auth_key()
if salt_auth_key is None: if salt_auth_key is None:
@ -65,55 +67,54 @@ def load_sd_salt() -> Optional[bytearray]:
salt_path = _get_salt_path() salt_path = _get_salt_path()
new_salt_path = _get_salt_path(new=True) new_salt_path = _get_salt_path(new=True)
with get_filesystem() as fs: salt = _load_salt(salt_auth_key, salt_path)
salt = _load_salt(fs, salt_auth_key, salt_path) if salt is not None:
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)
return salt 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: def set_sd_salt(salt: bytes, salt_tag: bytes, stage: bool = False) -> None:
salt_path = _get_salt_path(stage) salt_path = _get_salt_path(stage)
with get_filesystem() as fs: fatfs.mkdir("/trezor", True)
fs.mkdir("/trezor", True) fatfs.mkdir(_get_device_dir(), True)
fs.mkdir(_get_device_dir(), True) with fatfs.open(salt_path, "w") as f:
with fs.open(salt_path, "w") as f: f.write(salt)
f.write(salt) f.write(salt_tag)
f.write(salt_tag)
@with_filesystem
def commit_sd_salt() -> None: def commit_sd_salt() -> None:
salt_path = _get_salt_path(new=False) salt_path = _get_salt_path(new=False)
new_salt_path = _get_salt_path(new=True) new_salt_path = _get_salt_path(new=True)
with get_filesystem() as fs: try:
try: fatfs.unlink(salt_path)
fs.unlink(salt_path) except OSError:
except OSError: pass
pass fatfs.rename(new_salt_path, salt_path)
fs.rename(new_salt_path, salt_path)
@with_filesystem
def remove_sd_salt() -> None: def remove_sd_salt() -> None:
salt_path = _get_salt_path() salt_path = _get_salt_path()
with get_filesystem() as fs: # TODO Possibly overwrite salt file with random data.
# TODO Possibly overwrite salt file with random data. fatfs.unlink(salt_path)
fs.unlink(salt_path)

@ -1,2 +1,4 @@
import trezorconfig as config # noqa: F401 import trezorconfig as config # noqa: F401
import trezorio as io # noqa: F401 import trezorio as io # noqa: F401
fatfs = io.fatfs

@ -1,14 +1,15 @@
from trezorio import FatFS, sdcard from trezorio import fatfs, sdcard
if False: if False:
from typing import Any, Optional from typing import Any, Callable, Optional, TypeVar
T = TypeVar("T", bound=Callable)
class FilesystemWrapper: class FilesystemWrapper:
_INSTANCE = None # type: Optional[FilesystemWrapper] _INSTANCE = None # type: Optional[FilesystemWrapper]
def __init__(self, mounted: bool) -> None: def __init__(self, mounted: bool) -> None:
self.fs = FatFS()
self.mounted = mounted self.mounted = mounted
self.counter = 0 self.counter = 0
@ -21,20 +22,18 @@ class FilesystemWrapper:
return cls._INSTANCE return cls._INSTANCE
def _deinit_instance(self) -> None: def _deinit_instance(self) -> None:
if self.mounted: fatfs.unmount()
self.fs.unmount()
sdcard.power_off() sdcard.power_off()
FilesystemWrapper._INSTANCE = None FilesystemWrapper._INSTANCE = None
def __enter__(self) -> "FatFS": def __enter__(self) -> None:
try: try:
if self.counter <= 0: if self.counter <= 0:
self.counter = 0 self.counter = 0
sdcard.power_on() sdcard.power_on()
if self.mounted: if self.mounted:
self.fs.mount() fatfs.mount()
self.counter += 1 self.counter += 1
return self.fs
except Exception: except Exception:
self._deinit_instance() self._deinit_instance()
raise raise
@ -46,8 +45,17 @@ class FilesystemWrapper:
self._deinit_instance() self._deinit_instance()
def get_filesystem(mounted: bool = True) -> FilesystemWrapper: def filesystem(mounted: bool = True) -> FilesystemWrapper:
return FilesystemWrapper.get_instance(mounted=mounted) 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 is_present = sdcard.is_present
capacity = sdcard.capacity

@ -1,19 +1,17 @@
from common import * from common import *
from trezor import io from trezorio import sdcard, fatfs
class TestTrezorIoFatfs(unittest.TestCase): class TestTrezorIoFatfs(unittest.TestCase):
def setUp(self): def setUp(self):
io.sdcard.power_on() sdcard.power_on()
self.fs = io.FatFS() fatfs.mkfs()
self.fs.mkfs() fatfs.mount()
self.fs.mount()
def tearDown(self): def tearDown(self):
self.fs.unmount() fatfs.unmount()
io.sdcard.power_off() sdcard.power_off()
def _filename(self, suffix=""): def _filename(self, suffix=""):
return "FILE%s.TXT" % suffix return "FILE%s.TXT" % suffix
@ -26,78 +24,80 @@ class TestTrezorIoFatfs(unittest.TestCase):
pass pass
def test_mkdir(self): def test_mkdir(self):
self.fs.mkdir("/%s" % self._dirname()) fatfs.mkdir("/%s" % self._dirname())
s = self.fs.stat("/%s" % self._dirname()) s = fatfs.stat("/%s" % self._dirname())
self.assertEqual(s, (0, "---d-", self._dirname())) self.assertEqual(s, (0, "---d-", self._dirname()))
def test_listdir(self): def test_listdir(self):
self.fs.mkdir("/%s" % self._dirname()) fatfs.mkdir("/%s" % self._dirname())
with self.fs.open("/%s" % self._filename(), "w") as f: with fatfs.open("/%s" % self._filename(), "w") as f:
f.write(bytearray(b"test")) f.write(bytearray(b"test"))
with self.fs.open("/%s/%s" % (self._dirname(), self._filename("2")), "w") as f: with fatfs.open("/%s/%s" % (self._dirname(), self._filename("2")), "w") as f:
f.write(bytearray(b"testtest")) f.write(bytearray(b"testtest"))
l = [e for e in self.fs.listdir("/")] l = [e for e in fatfs.listdir("/")]
self.assertEqual(l, [(0, "---d-", self._dirname()), (4, "----a", self._filename())]) self.assertEqual(
l = [e for e in self.fs.listdir("/%s" % self._dirname())] l, [(0, "---d-", self._dirname()), (4, "----a", self._filename())]
)
l = [e for e in fatfs.listdir("/%s" % self._dirname())]
self.assertEqual(l, [(8, "----a", self._filename("2"))]) self.assertEqual(l, [(8, "----a", self._filename("2"))])
def test_unlink(self): def test_unlink(self):
self.fs.mkdir("/%s" % self._dirname()) fatfs.mkdir("/%s" % self._dirname())
with self.fs.open("/%s" % self._filename(), "w") as f: with fatfs.open("/%s" % self._filename(), "w") as f:
f.write(bytearray(b"test")) f.write(bytearray(b"test"))
s = self.fs.stat("/%s" % self._dirname()) s = fatfs.stat("/%s" % self._dirname())
self.assertEqual(s, (0, "---d-", self._dirname())) self.assertEqual(s, (0, "---d-", self._dirname()))
s = self.fs.stat("/%s" % self._filename()) s = fatfs.stat("/%s" % self._filename())
self.assertEqual(s, (4, "----a", self._filename())) self.assertEqual(s, (4, "----a", self._filename()))
self.fs.unlink("/%s" % self._dirname()) fatfs.unlink("/%s" % self._dirname())
self.fs.unlink("/%s" % self._filename()) fatfs.unlink("/%s" % self._filename())
with self.assertRaises(OSError): with self.assertRaises(OSError):
self.fs.stat("/%s" % self._dirname()) fatfs.stat("/%s" % self._dirname())
with self.assertRaises(OSError): with self.assertRaises(OSError):
self.assertRaises(self.fs.stat("/%s" % self._filename())) self.assertRaises(fatfs.stat("/%s" % self._filename()))
def test_rename(self): def test_rename(self):
self.fs.mkdir("/%s" % self._dirname()) fatfs.mkdir("/%s" % self._dirname())
with self.fs.open("/%s" % self._filename(), "w") as f: with fatfs.open("/%s" % self._filename(), "w") as f:
f.write(bytearray(b"test")) f.write(bytearray(b"test"))
s = self.fs.stat("/%s" % self._dirname()) s = fatfs.stat("/%s" % self._dirname())
self.assertEqual(s, (0, "---d-", self._dirname())) self.assertEqual(s, (0, "---d-", self._dirname()))
s = self.fs.stat("/%s" % self._filename()) s = fatfs.stat("/%s" % self._filename())
self.assertEqual(s, (4, "----a", self._filename())) self.assertEqual(s, (4, "----a", self._filename()))
self.fs.rename("/%s" % self._dirname(), "/%s" % self._dirname("2")) fatfs.rename("/%s" % self._dirname(), "/%s" % self._dirname("2"))
self.fs.rename("/%s" % self._filename(), "/%s" % self._filename("2")) fatfs.rename("/%s" % self._filename(), "/%s" % self._filename("2"))
with self.assertRaises(OSError): with self.assertRaises(OSError):
self.fs.stat("/%s" % self._dirname()) fatfs.stat("/%s" % self._dirname())
with self.assertRaises(OSError): with self.assertRaises(OSError):
self.assertRaises(self.fs.stat("/%s" % self._filename())) self.assertRaises(fatfs.stat("/%s" % self._filename()))
s = self.fs.stat("/%s" % self._dirname("2")) s = fatfs.stat("/%s" % self._dirname("2"))
self.assertEqual(s, (0, "---d-", self._dirname("2"))) self.assertEqual(s, (0, "---d-", self._dirname("2")))
s = self.fs.stat("/%s" % self._filename("2")) s = fatfs.stat("/%s" % self._filename("2"))
self.assertEqual(s, (4, "----a", self._filename("2"))) self.assertEqual(s, (4, "----a", self._filename("2")))
def test_open_rw(self): def test_open_rw(self):
with self.fs.open("/%s" % self._filename(), "w") as f: with fatfs.open("/%s" % self._filename(), "w") as f:
f.write(bytearray(b"test")) f.write(bytearray(b"test"))
with self.fs.open("/%s" % self._filename(), "r") as f: with fatfs.open("/%s" % self._filename(), "r") as f:
b = bytearray(100) b = bytearray(100)
r = f.read(b) r = f.read(b)
self.assertEqual(r, 4) self.assertEqual(r, 4)
self.assertEqual(bytes(b[:4]), b"test") self.assertEqual(bytes(b[:4]), b"test")
def test_open_a(self): def test_open_a(self):
with self.fs.open("/%s" % self._filename(), "w") as f: with fatfs.open("/%s" % self._filename(), "w") as f:
f.write(bytearray(b"test" * 200)) f.write(bytearray(b"test" * 200))
with self.fs.open("/%s" % self._filename(), "a") as f: with fatfs.open("/%s" % self._filename(), "a") as f:
f.seek(800) f.seek(800)
f.write(bytearray(b"TEST" * 200)) f.write(bytearray(b"TEST" * 200))
with self.fs.open("/%s" % self._filename(), "r") as f: with fatfs.open("/%s" % self._filename(), "r") as f:
b = bytearray(2000) b = bytearray(2000)
r = f.read(b) r = f.read(b)
self.assertEqual(r, 1600) self.assertEqual(r, 1600)
self.assertEqual(bytes(b[:1600]), b"test" * 200 + b"TEST" * 200) self.assertEqual(bytes(b[:1600]), b"test" * 200 + b"TEST" * 200)
def test_seek(self): def test_seek(self):
with self.fs.open("/%s" % self._filename(), "w+") as f: with fatfs.open("/%s" % self._filename(), "w+") as f:
f.write(bytearray(b"test" * 10)) f.write(bytearray(b"test" * 10))
f.seek(2) f.seek(2)
b = bytearray(8) b = bytearray(8)
@ -106,19 +106,18 @@ class TestTrezorIoFatfs(unittest.TestCase):
self.assertEqual(bytes(b[:8]), b"sttestte") self.assertEqual(bytes(b[:8]), b"sttestte")
def test_truncate(self): def test_truncate(self):
with self.fs.open("/%s" % self._filename(), "w") as f: with fatfs.open("/%s" % self._filename(), "w") as f:
f.write(bytearray(b"test" * 100)) f.write(bytearray(b"test" * 100))
s = self.fs.stat("/%s" % self._filename()) s = fatfs.stat("/%s" % self._filename())
self.assertEqual(s, (400, "----a", self._filename())) self.assertEqual(s, (400, "----a", self._filename()))
with self.fs.open("/%s" % self._filename(), "a") as f: with fatfs.open("/%s" % self._filename(), "a") as f:
f.seek(111) f.seek(111)
f.truncate() f.truncate()
s = self.fs.stat("/%s" % self._filename()) s = fatfs.stat("/%s" % self._filename())
self.assertEqual(s, (111, "----a", self._filename())) self.assertEqual(s, (111, "----a", self._filename()))
class TestTrezorIoFatfsLfn(TestTrezorIoFatfs): class TestTrezorIoFatfsLfn(TestTrezorIoFatfs):
def _filename(self, suffix=""): def _filename(self, suffix=""):
return "reallylongfilename%s.textfile" % suffix return "reallylongfilename%s.textfile" % suffix
@ -126,5 +125,116 @@ class TestTrezorIoFatfsLfn(TestTrezorIoFatfs):
return "reallylongdirname%s" % suffix return "reallylongdirname%s" % suffix
class TestTrezorIoFatfsMounting(unittest.TestCase):
MOUNTED_METHODS = [
("open", ("hello.txt", "w")),
("listdir", ("",)),
("mkdir", ("testdir",)),
("unlink", ("hello.txt",)),
("stat", ("testdir",)),
("rename", ("testdir", "newdir")),
("setlabel", ("label",)),
]
UNMOUNTED_METHODS = [
("mkfs", ()),
]
OTHER = {
"__name__",
"__class__",
"mount",
"unmount",
"is_mounted",
"FatFSFile",
"FatFSDir",
}
def setUp(self):
sdcard.power_on()
def tearDown(self):
sdcard.power_off()
def test_mount_unmount(self):
fatfs.mkfs()
self.assertFalse(fatfs.is_mounted())
fatfs.mount()
self.assertTrue(fatfs.is_mounted())
fatfs.mount()
self.assertTrue(fatfs.is_mounted())
fatfs.unmount()
self.assertFalse(fatfs.is_mounted())
def test_no_filesystem(self):
# trash FAT table
sdcard.write(0, bytes([0xFF] * sdcard.BLOCK_SIZE))
self.assertFalse(fatfs.is_mounted())
try:
fatfs.mount()
self.fail("should have raised")
except OSError as e:
self.assertEqual(e.args[0], 19) # ENODEV
self.assertFalse(fatfs.is_mounted())
def test_exhaustive(self):
all_symbols = (
set(name for name, call in (self.MOUNTED_METHODS + self.UNMOUNTED_METHODS))
| self.OTHER
)
self.assertEqual(set(dir(fatfs)), all_symbols)
def test_mounted(self):
fatfs.mkfs()
fatfs.mount()
self.assertTrue(fatfs.is_mounted())
for name, call in self.MOUNTED_METHODS:
function = getattr(fatfs, name)
function(*call)
for name, call in self.UNMOUNTED_METHODS:
function = getattr(fatfs, name)
try:
function(*call)
self.fail("should have raised")
except OSError as e:
self.assertEqual(e.args[0], 16) # EBUSY
def test_unmounted(self):
fatfs.unmount()
fatfs.mkfs()
self.assertFalse(fatfs.is_mounted())
for name, call in self.UNMOUNTED_METHODS:
function = getattr(fatfs, name)
function(*call)
self.assertFalse(fatfs.is_mounted())
for name, call in self.MOUNTED_METHODS:
function = getattr(fatfs, name)
try:
function(*call)
self.fail("should have raised")
except OSError as e:
self.assertEqual(e.args[0], 19) # ENODEV
class TestTrezorIoFatfsAndSdcard(unittest.TestCase):
def test_sd_power(self):
sdcard.power_off()
self.assertFalse(fatfs.is_mounted())
self.assertRaises(OSError, fatfs.mount)
sdcard.power_on()
self.assertFalse(fatfs.is_mounted())
fatfs.mkfs()
fatfs.mount()
self.assertTrue(fatfs.is_mounted())
sdcard.power_off()
self.assertFalse(fatfs.is_mounted())
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

@ -1,75 +1,68 @@
from common import * from common import *
from trezor import io, sdcard from trezor import io, fatfs, sdcard
class TestTrezorSdcard(unittest.TestCase): class TestTrezorSdcard(unittest.TestCase):
def test_power(self): 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 # non-zero value otherwise
self.assertEqual(io.sdcard.capacity(), 0) self.assertEqual(sdcard.capacity(), 0)
with sdcard.get_filesystem(mounted=False): with sdcard.filesystem(mounted=False):
self.assertTrue(io.sdcard.capacity() > 0) self.assertTrue(sdcard.capacity() > 0)
self.assertEqual(io.sdcard.capacity(), 0) self.assertEqual(sdcard.capacity(), 0)
def test_nomount(self): def test_nomount(self):
with sdcard.get_filesystem(mounted=False) as fs: with sdcard.filesystem(mounted=False):
with self.assertRaises(OSError): self.assertFalse(fatfs.is_mounted())
fs.listdir("/")
def test_mount(self): def test_mount(self):
# set up a filesystem first # set up a filesystem first
with sdcard.get_filesystem(mounted=False) as fs: with sdcard.filesystem(mounted=False):
fs.mkfs() fatfs.mkfs()
with sdcard.get_filesystem() as fs: with sdcard.filesystem():
# the following should succeed self.assertTrue(fatfs.is_mounted())
fs.listdir("/")
# filesystem should not be available self.assertFalse(fatfs.is_mounted())
with self.assertRaises(OSError):
fs.listdir("/")
def test_nesting(self): def test_nesting(self):
# set up a filesystem first # set up a filesystem first
with sdcard.get_filesystem(mounted=False) as fs: with sdcard.filesystem(mounted=False):
fs.mkfs() fatfs.mkfs()
self.assertEqual(io.sdcard.capacity(), 0) self.assertEqual(sdcard.capacity(), 0)
with sdcard.get_filesystem() as fs_a: with sdcard.filesystem():
self.assertTrue(io.sdcard.capacity() > 0) self.assertTrue(sdcard.capacity() > 0)
with sdcard.get_filesystem() as fs_b: self.assertTrue(fatfs.is_mounted())
self.assertTrue(io.sdcard.capacity() > 0) with sdcard.filesystem():
self.assertIs(fs_a, fs_b) self.assertTrue(sdcard.capacity() > 0)
fs_b.listdir("/") self.assertTrue(fatfs.is_mounted())
self.assertTrue(io.sdcard.capacity() > 0)
# filesystem should still be mounted self.assertTrue(sdcard.capacity() > 0)
fs_a.listdir("/") self.assertTrue(fatfs.is_mounted())
self.assertEqual(io.sdcard.capacity(), 0) self.assertEqual(sdcard.capacity(), 0)
# filesystem should not be available self.assertFalse(fatfs.is_mounted())
with self.assertRaises(OSError):
fs_a.listdir("/")
def test_mount_nomount(self): def test_mount_nomount(self):
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
with sdcard.get_filesystem(mounted=True): with sdcard.filesystem(mounted=True):
with sdcard.get_filesystem(mounted=False): with sdcard.filesystem(mounted=False):
pass pass
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
with sdcard.get_filesystem(mounted=False): with sdcard.filesystem(mounted=False):
with sdcard.get_filesystem(mounted=True): with sdcard.filesystem(mounted=True):
pass pass
def test_failed_mount(self): def test_failed_mount(self):
# set up a filesystem first # set up a filesystem first
with sdcard.get_filesystem(mounted=False) as fs: with sdcard.filesystem(mounted=False):
fs.mkfs() fatfs.mkfs()
with sdcard.get_filesystem() as fs: with sdcard.filesystem():
# the following should succeed self.assertTrue(fatfs.is_mounted())
fs.listdir("/")
# trash filesystem # trash filesystem
io.sdcard.power_on() io.sdcard.power_on()
@ -78,17 +71,18 @@ class TestTrezorSdcard(unittest.TestCase):
# mounting should now fail # mounting should now fail
with self.assertRaises(OSError): with self.assertRaises(OSError):
with sdcard.get_filesystem() as fs: with sdcard.filesystem():
pass pass
self.assertFalse(fatfs.is_mounted())
# it should be possible to create an unmounted instance # it should be possible to create an unmounted instance
with sdcard.get_filesystem(mounted=False) as fs: with sdcard.filesystem(mounted=False):
fs.mkfs() fatfs.mkfs()
# mounting should now succeed # mounting should now succeed
with sdcard.get_filesystem() as fs: with sdcard.filesystem():
fs.listdir("/") self.assertTrue(fatfs.is_mounted())
if __name__ == "__main__": if __name__ == "__main__":

Loading…
Cancel
Save