2021-12-06 12:17:20 -05:00
|
|
|
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
2021-09-21 18:32:54 -04:00
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2022-04-04 09:33:00 -04:00
|
|
|
from typing import List
|
|
|
|
|
2021-09-21 18:32:54 -04:00
|
|
|
from .cluster import Cluster
|
|
|
|
from .exceptions import NoFreeClusterException
|
2022-04-04 09:33:00 -04:00
|
|
|
from .fatfs_state import BootSectorState
|
2021-09-21 18:32:54 -04:00
|
|
|
|
|
|
|
|
|
|
|
class FAT:
|
|
|
|
"""
|
|
|
|
The FAT represents the FAT region in file system. It is responsible for storing clusters
|
|
|
|
and chaining them in case we need to extend file or directory to more clusters.
|
|
|
|
"""
|
2022-04-04 09:33:00 -04:00
|
|
|
|
|
|
|
def allocate_root_dir(self) -> None:
|
|
|
|
"""
|
|
|
|
The root directory is implicitly created with the FatFS,
|
|
|
|
its block is on the index 1 (second index) and is allocated implicitly.
|
|
|
|
"""
|
|
|
|
self.clusters[Cluster.ROOT_BLOCK_ID].allocate_cluster()
|
|
|
|
|
|
|
|
def __init__(self, boot_sector_state: BootSectorState, init_: bool) -> None:
|
2022-06-29 09:37:06 -04:00
|
|
|
self._first_free_cluster_id = 0
|
2022-04-04 09:33:00 -04:00
|
|
|
self.boot_sector_state = boot_sector_state
|
|
|
|
self.clusters: List[Cluster] = [Cluster(cluster_id=i,
|
|
|
|
boot_sector_state=self.boot_sector_state,
|
|
|
|
init_=init_) for i in range(self.boot_sector_state.clusters)]
|
|
|
|
if init_:
|
|
|
|
self.allocate_root_dir()
|
|
|
|
|
2022-04-04 09:33:00 -04:00
|
|
|
def get_cluster_value(self, cluster_id_: int) -> int:
|
|
|
|
fat_cluster_value_: int = self.clusters[cluster_id_].get_from_fat()
|
|
|
|
return fat_cluster_value_
|
|
|
|
|
|
|
|
def is_cluster_last(self, cluster_id_: int) -> bool:
|
|
|
|
value_ = self.get_cluster_value(cluster_id_)
|
|
|
|
is_cluster_last_: bool = value_ == (1 << self.boot_sector_state.fatfs_type) - 1
|
|
|
|
return is_cluster_last_
|
2021-09-21 18:32:54 -04:00
|
|
|
|
2022-05-23 09:10:46 -04:00
|
|
|
def chain_content(self, cluster_id_: int) -> bytearray:
|
|
|
|
bin_im: bytearray = self.boot_sector_state.binary_image
|
|
|
|
if self.is_cluster_last(cluster_id_):
|
|
|
|
data_address_ = Cluster.compute_cluster_data_address(self.boot_sector_state, cluster_id_)
|
|
|
|
content_: bytearray = bin_im[data_address_: data_address_ + self.boot_sector_state.sector_size]
|
|
|
|
return content_
|
|
|
|
fat_value_: int = self.get_cluster_value(cluster_id_)
|
|
|
|
data_address_ = Cluster.compute_cluster_data_address(self.boot_sector_state, cluster_id_)
|
|
|
|
content_ = bin_im[data_address_: data_address_ + self.boot_sector_state.sector_size]
|
|
|
|
|
|
|
|
while not self.is_cluster_last(cluster_id_):
|
|
|
|
cluster_id_ = fat_value_
|
|
|
|
fat_value_ = self.get_cluster_value(cluster_id_)
|
|
|
|
data_address_ = Cluster.compute_cluster_data_address(self.boot_sector_state, cluster_id_)
|
|
|
|
content_ += bin_im[data_address_: data_address_ + self.boot_sector_state.sector_size]
|
|
|
|
return content_
|
|
|
|
|
2021-09-21 18:32:54 -04:00
|
|
|
def find_free_cluster(self) -> Cluster:
|
|
|
|
# finds first empty cluster and allocates it
|
2022-06-29 09:37:06 -04:00
|
|
|
for cluster_id, cluster in enumerate(self.clusters[self._first_free_cluster_id:],
|
|
|
|
start=self._first_free_cluster_id):
|
2021-09-21 18:32:54 -04:00
|
|
|
if cluster.is_empty:
|
|
|
|
cluster.allocate_cluster()
|
2022-06-29 09:37:06 -04:00
|
|
|
self._first_free_cluster_id = cluster_id
|
2021-09-21 18:32:54 -04:00
|
|
|
return cluster
|
|
|
|
raise NoFreeClusterException('No free cluster available!')
|
|
|
|
|
|
|
|
def allocate_chain(self, first_cluster: Cluster, size: int) -> None:
|
|
|
|
current = first_cluster
|
|
|
|
for _ in range(size - 1):
|
|
|
|
free_cluster = self.find_free_cluster()
|
|
|
|
current.next_cluster = free_cluster
|
|
|
|
current.set_in_fat(free_cluster.id)
|
|
|
|
current = free_cluster
|