fatfsparse.py: add cluster chaining support to enable reading longer-than-sector files

Closes IDF-4975
This commit is contained in:
Martin Gano
2022-04-04 15:33:00 +02:00
committed by Martin Gaňo
parent 64b6733349
commit 8ef66536ed
18 changed files with 368 additions and 62 deletions

View File

@@ -4,40 +4,77 @@ import os
import sys
from typing import Tuple
from fatfsgen_utils.boot_sector import BootSector
from fatfsgen_utils.cluster import Cluster
from fatfsgen_utils.entry import Entry
from fatfsgen_utils.fat import FAT
from fatfsgen_utils.fatfs_state import BootSectorState
from fatfsgen_utils.utils import PAD_CHAR, FATDefaults, read_filesystem
from fatfs_utils.boot_sector import BootSector
from fatfs_utils.cluster import Cluster
from fatfs_utils.entry import Entry
from fatfs_utils.fat import FAT
from fatfs_utils.fatfs_state import BootSectorState
from fatfs_utils.utils import PAD_CHAR, FATDefaults, read_filesystem
def get_address_and_name(obj_: dict, state_: BootSectorState) -> Tuple[int, str]:
def get_chained_full_content(cluster_id_: int,
fat_: FAT,
state_: BootSectorState,
binary_array_: bytearray) -> bytearray:
if fat_.is_cluster_last(cluster_id_):
data_address_ = Cluster.compute_cluster_data_address(state_, cluster_id_)
content_: bytearray = binary_array_[data_address_: data_address_ + state_.sector_size]
return content_
fat_value_: int = fat_.get_cluster_value(cluster_id_)
data_address_ = Cluster.compute_cluster_data_address(state_, cluster_id_)
content_ = binary_array_[data_address_: data_address_ + state_.sector_size]
while not fat_.is_cluster_last(cluster_id_):
cluster_id_ = fat_value_
fat_value_ = fat_.get_cluster_value(cluster_id_)
data_address_ = Cluster.compute_cluster_data_address(state_, cluster_id_)
content_ += binary_array_[data_address_: data_address_ + state_.sector_size]
return content_
def get_name_and_id(obj_: dict) -> Tuple[str, int]:
cluster_id_ = obj_['DIR_FstClusLO']
obj_ext_ = obj_['DIR_Name_ext'].rstrip(chr(PAD_CHAR))
ext_ = f'.{obj_ext_}' if len(obj_ext_) > 0 else ''
obj_name_ = obj_['DIR_Name'].rstrip(chr(PAD_CHAR)) + ext_
data_address_ = Cluster.compute_cluster_data_address(state_, cluster_id_)
return data_address_, obj_name_
return obj_name_, cluster_id_
def traverse_folder_tree(directory_address: int, name: str, state_: BootSectorState) -> None:
def traverse_folder_tree(directory_bytes_: bytes,
name: str,
state_: BootSectorState, fat_: FAT,
binary_array_: bytearray) -> None:
if name not in ('.', '..'):
os.makedirs(name)
for i in range(state_.sector_size // FATDefaults.ENTRY_SIZE):
obj_address_ = directory_address + FATDefaults.ENTRY_SIZE * i
for i in range(len(directory_bytes_) // FATDefaults.ENTRY_SIZE):
obj_address_ = FATDefaults.ENTRY_SIZE * i
obj_ = Entry.ENTRY_FORMAT_SHORT_NAME.parse(
fs[obj_address_: obj_address_ + FATDefaults.ENTRY_SIZE])
directory_bytes_[obj_address_: obj_address_ + FATDefaults.ENTRY_SIZE])
if obj_['DIR_Attr'] == Entry.ATTR_ARCHIVE:
data_address_, obj_name_ = get_address_and_name(obj_, state_)
content_ = fs[data_address_: data_address_ + state_.sector_size].rstrip(chr(0x00).encode())
obj_name_, cluster_id_ = get_name_and_id(obj_)
content_ = get_chained_full_content(
cluster_id_=cluster_id_,
fat_=fat_,
state_=state_,
binary_array_=binary_array_
).rstrip(chr(0x00).encode())
with open(os.path.join(name, obj_name_), 'wb') as new_file:
new_file.write(content_)
elif obj_['DIR_Attr'] == Entry.ATTR_DIRECTORY:
data_address_, obj_name_ = get_address_and_name(obj_, state_)
obj_name_, cluster_id_ = get_name_and_id(obj_)
if obj_name_ in ('.', '..'):
continue
traverse_folder_tree(data_address_, os.path.join(name, obj_name_), state_=state_)
child_directory_bytes_ = get_chained_full_content(
cluster_id_=obj_['DIR_FstClusLO'],
fat_=fat_,
state_=state_,
binary_array_=binary_array_
)
traverse_folder_tree(directory_bytes_=child_directory_bytes_,
name=os.path.join(name, obj_name_),
state_=state_,
fat_=fat_,
binary_array_=binary_array_)
if __name__ == '__main__':
@@ -46,6 +83,9 @@ if __name__ == '__main__':
parser.parse_boot_sector(fs)
fat = FAT(parser.boot_sector_state, init_=False)
traverse_folder_tree(parser.boot_sector_state.root_directory_start,
boot_dir_start_ = parser.boot_sector_state.root_directory_start
boot_dir_sectors = parser.boot_sector_state.root_dir_sectors_cnt
full_ = fs[boot_dir_start_: boot_dir_start_ + boot_dir_sectors * parser.boot_sector_state.sector_size]
traverse_folder_tree(full_,
parser.boot_sector_state.volume_label.rstrip(chr(PAD_CHAR)),
parser.boot_sector_state)
parser.boot_sector_state, fat, fs)