fix(fatfs): Fixed fatfsparse.py parsing of FAT boot sector

The fatfsparse.py script was too strict in parsing the FAT boot sector, causing it to fail in
certain cases. This commit fixes the issue by making the parsing less strict and allowing for more
flexibility in the boot sector format.

This change improves the reliability and compatibility of the fatfsparse.py script, ensuring that it
can correctly parse a wider range of FAT boot sectors.

Docs updated
This commit is contained in:
radek.tandler
2023-11-02 11:33:47 +01:00
parent 0e69fcb8ac
commit a640626b76
4 changed files with 21 additions and 13 deletions

View File

@@ -1,9 +1,9 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
from inspect import getmembers, isroutine from inspect import getmembers, isroutine
from typing import Optional from typing import Optional
from construct import Const, Int8ul, Int16ul, Int32ul, PaddedString, Struct, core from construct import Bytes, Const, Int8ul, Int16ul, Int32ul, PaddedString, Padding, Struct, core
from .exceptions import InconsistentFATAttributes, NotInitialized from .exceptions import InconsistentFATAttributes, NotInitialized
from .fatfs_state import BootSectorState from .fatfs_state import BootSectorState
@@ -29,8 +29,7 @@ class BootSector:
BOOT_HEADER_SIZE = 512 BOOT_HEADER_SIZE = 512
BOOT_SECTOR_HEADER = Struct( BOOT_SECTOR_HEADER = Struct(
# this value reflects BS_jmpBoot used for ESP32 boot sector (any other accepted) 'BS_jmpBoot' / Bytes(3),
'BS_jmpBoot' / Const(b'\xeb\xfe\x90'),
'BS_OEMName' / PaddedString(MAX_OEM_NAME_SIZE, SHORT_NAMES_ENCODING), 'BS_OEMName' / PaddedString(MAX_OEM_NAME_SIZE, SHORT_NAMES_ENCODING),
'BPB_BytsPerSec' / Int16ul, 'BPB_BytsPerSec' / Int16ul,
'BPB_SecPerClus' / Int8ul, 'BPB_SecPerClus' / Int8ul,
@@ -45,12 +44,12 @@ class BootSector:
'BPB_HiddSec' / Int32ul, 'BPB_HiddSec' / Int32ul,
'BPB_TotSec32' / Int32ul, # zero if the FAT type is 12/16, otherwise number of sectors 'BPB_TotSec32' / Int32ul, # zero if the FAT type is 12/16, otherwise number of sectors
'BS_DrvNum' / Const(b'\x80'), 'BS_DrvNum' / Const(b'\x80'),
'BS_Reserved1' / Const(EMPTY_BYTE), 'BS_Reserved1' / Padding(1),
'BS_BootSig' / Const(b'\x29'), 'BS_BootSig' / Const(b'\x29'),
'BS_VolID' / Int32ul, 'BS_VolID' / Int32ul,
'BS_VolLab' / PaddedString(MAX_VOL_LAB_SIZE, SHORT_NAMES_ENCODING), 'BS_VolLab' / PaddedString(MAX_VOL_LAB_SIZE, SHORT_NAMES_ENCODING),
'BS_FilSysType' / PaddedString(MAX_FS_TYPE_SIZE, SHORT_NAMES_ENCODING), 'BS_FilSysType' / PaddedString(MAX_FS_TYPE_SIZE, SHORT_NAMES_ENCODING),
'BS_EMPTY' / Const(448 * EMPTY_BYTE), 'BS_EMPTY' / Padding(448),
'Signature_word' / Const(FATDefaults.SIGNATURE_WORD) 'Signature_word' / Const(FATDefaults.SIGNATURE_WORD)
) )
assert BOOT_SECTOR_HEADER.sizeof() == BOOT_HEADER_SIZE assert BOOT_SECTOR_HEADER.sizeof() == BOOT_HEADER_SIZE
@@ -73,7 +72,8 @@ class BootSector:
* EMPTY_BYTE) * EMPTY_BYTE)
self.boot_sector_state.binary_image = ( self.boot_sector_state.binary_image = (
BootSector.BOOT_SECTOR_HEADER.build( BootSector.BOOT_SECTOR_HEADER.build(
dict(BS_OEMName=pad_string(boot_sector_state.oem_name, size=BootSector.MAX_OEM_NAME_SIZE), dict(BS_jmpBoot=(b'\xeb\xfe\x90'),
BS_OEMName=pad_string(boot_sector_state.oem_name, size=BootSector.MAX_OEM_NAME_SIZE),
BPB_BytsPerSec=boot_sector_state.sector_size, BPB_BytsPerSec=boot_sector_state.sector_size,
BPB_SecPerClus=boot_sector_state.sectors_per_cluster, BPB_SecPerClus=boot_sector_state.sectors_per_cluster,
BPB_RsvdSecCnt=boot_sector_state.reserved_sectors_cnt, BPB_RsvdSecCnt=boot_sector_state.reserved_sectors_cnt,
@@ -91,8 +91,7 @@ class BootSector:
BS_VolLab=pad_string(boot_sector_state.volume_label, BS_VolLab=pad_string(boot_sector_state.volume_label,
size=BootSector.MAX_VOL_LAB_SIZE), size=BootSector.MAX_VOL_LAB_SIZE),
BS_FilSysType=pad_string(boot_sector_state.file_sys_type, BS_FilSysType=pad_string(boot_sector_state.file_sys_type,
size=BootSector.MAX_FS_TYPE_SIZE) size=BootSector.MAX_FS_TYPE_SIZE))
)
) + pad_header + fat_tables_content + root_dir_content + data_content ) + pad_header + fat_tables_content + root_dir_content + data_content
) )

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
import argparse import argparse
import os import os
@@ -100,7 +100,7 @@ def remove_wear_levelling_if_exists(fs_: bytes) -> bytes:
boot_sector__.parse_boot_sector(fs_) boot_sector__.parse_boot_sector(fs_)
if boot_sector__.boot_sector_state.size == len(fs_): if boot_sector__.boot_sector_state.size == len(fs_):
return fs_ return fs_
except construct.core.ConstError: except UnicodeDecodeError:
pass pass
plain_fs: bytes = remove_wl(fs_) plain_fs: bytes = remove_wl(fs_)
return plain_fs return plain_fs
@@ -124,6 +124,9 @@ if __name__ == '__main__':
default=None, default=None,
help="If detection doesn't work correctly, " help="If detection doesn't work correctly, "
'you can force analyzer to or not to assume WL.') 'you can force analyzer to or not to assume WL.')
argument_parser.add_argument('--verbose',
action='store_true',
help='Prints details about FAT image.')
args = argument_parser.parse_args() args = argument_parser.parse_args()
@@ -157,6 +160,10 @@ if __name__ == '__main__':
boot_sector_ = BootSector() boot_sector_ = BootSector()
boot_sector_.parse_boot_sector(fs) boot_sector_.parse_boot_sector(fs)
if args.verbose:
print(str(boot_sector_))
fat = FAT(boot_sector_.boot_sector_state, init_=False) fat = FAT(boot_sector_.boot_sector_state, init_=False)
boot_dir_start_ = boot_sector_.boot_sector_state.root_directory_start boot_dir_start_ = boot_sector_.boot_sector_state.root_directory_start

View File

@@ -136,8 +136,9 @@ It is a reverse tool of (:component_file:`fatfsgen.py <fatfs/fatfsgen.py>`), i.e
Usage:: Usage::
./fatfsparse.py [-h] [--wl-layer {detect,enabled,disabled}] fatfs_image.img ./fatfsparse.py [-h] [--wl-layer {detect,enabled,disabled}] [--verbose] fatfs_image.img
Parameter --verbose prints detailed information from boot sector of the FatFs image to the terminal before folder structure is generated.
High-level API Reference High-level API Reference
------------------------ ------------------------

View File

@@ -136,8 +136,9 @@ FatFs 分区分析器
可以使用:: 可以使用::
./fatfsparse.py [-h] [--wl-layer {detect,enabled,disabled}] fatfs_image.img ./fatfsparse.py [-h] [--wl-layer {detect,enabled,disabled}] [--verbose] fatfs_image.img
生成文件夹结构之前,参数 --verbose 将根据 FatFs 镜像的引导扇区在终端打印详细信息。
高级 API 参考 高级 API 参考
------------------------ ------------------------