From 21408698e16b8e383e5e39436efbf45615def86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ga=C5=88o?= Date: Wed, 14 Sep 2022 12:26:25 +0200 Subject: [PATCH] fatfsgen.py: Support for detection of minimal partition size --- components/fatfs/fatfs_utils/utils.py | 7 +++- components/fatfs/fatfsgen.py | 47 +++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/components/fatfs/fatfs_utils/utils.py b/components/fatfs/fatfs_utils/utils.py index 3b516a99201..d2180c76bda 100644 --- a/components/fatfs/fatfs_utils/utils.py +++ b/components/fatfs/fatfs_utils/utils.py @@ -14,6 +14,7 @@ # the regex pattern defines symbols that are allowed by long file names but not by short file names INVALID_SFN_CHARS_PATTERN = re.compile(r'[.+,;=\[\]]') +FATFS_MIN_ALLOC_UNIT: int = 128 FAT12_MAX_CLUSTERS: int = 4085 FAT16_MAX_CLUSTERS: int = 65525 RESERVED_CLUSTERS_COUNT: int = 2 @@ -174,7 +175,9 @@ def get_args_for_partition_generator(desc: str, wl: bool) -> argparse.Namespace: help='Filename of the generated fatfs image') parser.add_argument('--partition_size', default=FATDefaults.SIZE, - help='Size of the partition in bytes') + help='Size of the partition in bytes.' + + ('' if wl else ' Use `--partition_size detect` for detecting the minimal partition size.') + ) parser.add_argument('--sector_size', default=FATDefaults.SECTOR_SIZE, type=int, @@ -207,6 +210,8 @@ def get_args_for_partition_generator(desc: str, wl: bool) -> argparse.Namespace: args = parser.parse_args() if args.fat_type == 0: args.fat_type = None + if args.partition_size == 'detect' and not wl: + args.partition_size = -1 args.partition_size = int(str(args.partition_size), 0) if not os.path.isdir(args.input_directory): raise NotADirectoryError(f'The target directory `{args.input_directory}` does not exist!') diff --git a/components/fatfs/fatfsgen.py b/components/fatfs/fatfsgen.py index f01be09815d..7518b010707 100755 --- a/components/fatfs/fatfsgen.py +++ b/components/fatfs/fatfsgen.py @@ -7,11 +7,15 @@ from typing import Any, List, Optional from fatfs_utils.boot_sector import BootSector +from fatfs_utils.exceptions import NoFreeClusterException from fatfs_utils.fat import FAT from fatfs_utils.fatfs_state import FATFSState from fatfs_utils.fs_object import Directory -from fatfs_utils.utils import (BYTES_PER_DIRECTORY_ENTRY, FATFS_INCEPTION, FATDefaults, - get_args_for_partition_generator, read_filesystem) +from fatfs_utils.long_filename_utils import get_required_lfn_entries_count +from fatfs_utils.utils import (BYTES_PER_DIRECTORY_ENTRY, FATFS_INCEPTION, FATFS_MIN_ALLOC_UNIT, + RESERVED_CLUSTERS_COUNT, FATDefaults, get_args_for_partition_generator, + get_fat_sectors_count, get_non_data_sectors_cnt, read_filesystem, + required_clusters_count) class FATFS: @@ -184,8 +188,47 @@ def generate(self, input_directory: str) -> None: self._generate_partition_from_folder(folder_name, folder_path=path_to_folder, is_dir=True) +def calculate_min_space(path: List[str], + fs_entity: str, + sector_size: int = 0x1000, + long_file_names: bool = False, + is_root: bool = False) -> int: + if os.path.isfile(os.path.join(*path, fs_entity)): + with open(os.path.join(*path, fs_entity), 'rb') as file_: + content = file_.read() + res: int = required_clusters_count(sector_size, content) + return res + buff: int = 0 + dir_size = 2 * FATDefaults.ENTRY_SIZE # record for symlinks "." and ".." + for file in sorted(os.listdir(os.path.join(*path, fs_entity))): + if long_file_names and True: + # LFN entries + one short entry + dir_size += (get_required_lfn_entries_count(fs_entity) + 1) * FATDefaults.ENTRY_SIZE + else: + dir_size += FATDefaults.ENTRY_SIZE + buff += calculate_min_space(path + [fs_entity], file, sector_size, long_file_names, is_root=False) + if is_root and dir_size // FATDefaults.ENTRY_SIZE > FATDefaults.ROOT_ENTRIES_COUNT: + raise NoFreeClusterException('Not enough space in root!') + + # roundup sectors, at least one is required + buff += (dir_size + sector_size - 1) // sector_size + return buff + + def main() -> None: args = get_args_for_partition_generator('Create a FAT filesystem and populate it with directory content', wl=False) + + if args.partition_size == -1: + clusters = calculate_min_space([], args.input_directory, args.sector_size, long_file_names=True, is_root=True) + fats = get_fat_sectors_count(clusters, args.sector_size) + root_dir_sectors = (FATDefaults.ROOT_ENTRIES_COUNT * FATDefaults.ENTRY_SIZE) // args.sector_size + args.partition_size = max(FATFS_MIN_ALLOC_UNIT * args.sector_size, + (clusters + fats + get_non_data_sectors_cnt(RESERVED_CLUSTERS_COUNT, + fats, + root_dir_sectors) + ) * args.sector_size + ) + fatfs = FATFS(sector_size=args.sector_size, sectors_per_cluster=args.sectors_per_cluster, size=args.partition_size,