fat.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. # SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
  2. # SPDX-License-Identifier: Apache-2.0
  3. from typing import List
  4. from .cluster import Cluster
  5. from .exceptions import NoFreeClusterException
  6. from .fatfs_state import BootSectorState
  7. class FAT:
  8. """
  9. The FAT represents the FAT region in file system. It is responsible for storing clusters
  10. and chaining them in case we need to extend file or directory to more clusters.
  11. """
  12. def allocate_root_dir(self) -> None:
  13. """
  14. The root directory is implicitly created with the FatFS,
  15. its block is on the index 1 (second index) and is allocated implicitly.
  16. """
  17. self.clusters[Cluster.ROOT_BLOCK_ID].allocate_cluster()
  18. def __init__(self, boot_sector_state: BootSectorState, init_: bool) -> None:
  19. self._first_free_cluster_id = 0
  20. self.boot_sector_state = boot_sector_state
  21. self.clusters: List[Cluster] = [Cluster(cluster_id=i,
  22. boot_sector_state=self.boot_sector_state,
  23. init_=init_) for i in range(self.boot_sector_state.clusters)]
  24. if init_:
  25. self.allocate_root_dir()
  26. def get_cluster_value(self, cluster_id_: int) -> int:
  27. fat_cluster_value_: int = self.clusters[cluster_id_].get_from_fat()
  28. return fat_cluster_value_
  29. def is_cluster_last(self, cluster_id_: int) -> bool:
  30. value_ = self.get_cluster_value(cluster_id_)
  31. is_cluster_last_: bool = value_ == (1 << self.boot_sector_state.fatfs_type) - 1
  32. return is_cluster_last_
  33. def chain_content(self, cluster_id_: int) -> bytearray:
  34. bin_im: bytearray = self.boot_sector_state.binary_image
  35. if self.is_cluster_last(cluster_id_):
  36. data_address_ = Cluster.compute_cluster_data_address(self.boot_sector_state, cluster_id_)
  37. content_: bytearray = bin_im[data_address_: data_address_ + self.boot_sector_state.sector_size]
  38. return content_
  39. fat_value_: int = self.get_cluster_value(cluster_id_)
  40. data_address_ = Cluster.compute_cluster_data_address(self.boot_sector_state, cluster_id_)
  41. content_ = bin_im[data_address_: data_address_ + self.boot_sector_state.sector_size]
  42. while not self.is_cluster_last(cluster_id_):
  43. cluster_id_ = fat_value_
  44. fat_value_ = self.get_cluster_value(cluster_id_)
  45. data_address_ = Cluster.compute_cluster_data_address(self.boot_sector_state, cluster_id_)
  46. content_ += bin_im[data_address_: data_address_ + self.boot_sector_state.sector_size]
  47. return content_
  48. def find_free_cluster(self) -> Cluster:
  49. # finds first empty cluster and allocates it
  50. for cluster_id, cluster in enumerate(self.clusters[self._first_free_cluster_id:],
  51. start=self._first_free_cluster_id):
  52. if cluster.is_empty:
  53. cluster.allocate_cluster()
  54. self._first_free_cluster_id = cluster_id
  55. return cluster
  56. raise NoFreeClusterException('No free cluster available!')
  57. def allocate_chain(self, first_cluster: Cluster, size: int) -> None:
  58. current = first_cluster
  59. for _ in range(size - 1):
  60. free_cluster = self.find_free_cluster()
  61. current.next_cluster = free_cluster
  62. current.set_in_fat(free_cluster.id)
  63. current = free_cluster