embedded_fat/filesystem/directory.rs
1use core::convert::TryFrom;
2
3use crate::blockdevice::BlockIdx;
4use crate::fat::{FatType, OnDiskDirEntry};
5use crate::filesystem::{Attributes, ClusterId, SearchId, ShortFileName, Timestamp};
6
7/// Represents a directory entry, which tells you about
8/// other files and directories.
9#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
10#[derive(Debug, PartialEq, Eq, Clone)]
11pub struct DirEntry {
12 /// The name of the file
13 pub name: ShortFileName,
14 /// When the file was last modified
15 pub mtime: Timestamp,
16 /// When the file was first created
17 pub ctime: Timestamp,
18 /// The file attributes (Read Only, Archive, etc)
19 pub attributes: Attributes,
20 /// The starting cluster of the file. The FAT tells us the following Clusters.
21 pub cluster: ClusterId,
22 /// The size of the file in bytes.
23 pub size: u32,
24 /// The disk block of this entry
25 pub entry_block: BlockIdx,
26 /// The offset on its block (in bytes)
27 pub entry_offset: u32,
28}
29
30/// Represents an open directory on disk.
31///
32/// Do NOT drop this object! It doesn't hold a reference to the Volume Manager
33/// it was created from and if you drop it, the VolumeManager will think you
34/// still have the directory open, and it won't let you open the directory
35/// again.
36///
37/// Instead you must pass it to [`crate::VolumeManager::close_dir`] to close it
38/// cleanly.
39///
40/// If you want your directories to close themselves on drop, create your own
41/// `Directory` type that wraps this one and also holds a `VolumeManager`
42/// reference. You'll then also need to put your `VolumeManager` in some kind of
43/// Mutex or RefCell, and deal with the fact you can't put them both in the same
44/// struct any more because one refers to the other. Basically, it's complicated
45/// and there's a reason we did it this way.
46#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
47#[derive(Debug, Copy, Clone, PartialEq, Eq)]
48pub struct Directory(pub(crate) SearchId);
49
50/// Holds information about an open file on disk
51#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
52#[derive(Debug, Clone)]
53pub(crate) struct DirectoryInfo {
54 /// Unique ID for this directory.
55 pub(crate) directory_id: Directory,
56 /// The starting point of the directory listing.
57 pub(crate) cluster: ClusterId,
58}
59
60impl DirEntry {
61 pub(crate) fn serialize(&self, fat_type: FatType) -> [u8; OnDiskDirEntry::LEN] {
62 let mut data = [0u8; OnDiskDirEntry::LEN];
63 data[0..11].copy_from_slice(&self.name.contents);
64 data[11] = self.attributes.0;
65 // 12: Reserved. Must be set to zero
66 // 13: CrtTimeTenth, not supported, set to zero
67 data[14..18].copy_from_slice(&self.ctime.serialize_to_fat()[..]);
68 // 0 + 18: LastAccDate, not supported, set to zero
69 let cluster_number = self.cluster.0;
70 let cluster_hi = if fat_type == FatType::Fat16 {
71 [0u8; 2]
72 } else {
73 // Safe due to the AND operation
74 u16::try_from((cluster_number >> 16) & 0x0000_FFFF)
75 .unwrap()
76 .to_le_bytes()
77 };
78 data[20..22].copy_from_slice(&cluster_hi[..]);
79 data[22..26].copy_from_slice(&self.mtime.serialize_to_fat()[..]);
80 // Safe due to the AND operation
81 let cluster_lo = u16::try_from(cluster_number & 0x0000_FFFF)
82 .unwrap()
83 .to_le_bytes();
84 data[26..28].copy_from_slice(&cluster_lo[..]);
85 data[28..32].copy_from_slice(&self.size.to_le_bytes()[..]);
86 data
87 }
88
89 pub(crate) fn new(
90 name: ShortFileName,
91 attributes: Attributes,
92 cluster: ClusterId,
93 ctime: Timestamp,
94 entry_block: BlockIdx,
95 entry_offset: u32,
96 ) -> Self {
97 Self {
98 name,
99 mtime: ctime,
100 ctime,
101 attributes,
102 cluster,
103 size: 0,
104 entry_block,
105 entry_offset,
106 }
107 }
108}