embedded_fat/fat/
ondiskdirentry.rs

1//! Directory Entry as stored on-disk
2
3use crate::{
4    fat::{FatType, LfnEntry},
5    Attributes, BlockIdx, ClusterId, DirEntry, ShortFileName, Timestamp,
6};
7use byteorder::{ByteOrder, LittleEndian};
8
9/// Represents a 32-byte directory entry as stored on-disk in a directory file.
10pub struct OnDiskDirEntry<'a> {
11    data: &'a [u8],
12}
13
14impl<'a> core::fmt::Debug for OnDiskDirEntry<'a> {
15    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
16        write!(f, "OnDiskDirEntry<")?;
17        write!(f, "raw_attr = {}", self.raw_attr())?;
18        write!(f, ", create_time = {}", self.create_time())?;
19        write!(f, ", create_date = {}", self.create_date())?;
20        write!(f, ", last_access_data = {}", self.last_access_data())?;
21        write!(f, ", first_cluster_hi = {}", self.first_cluster_hi())?;
22        write!(f, ", write_time = {}", self.write_time())?;
23        write!(f, ", write_date = {}", self.write_date())?;
24        write!(f, ", first_cluster_lo = {}", self.first_cluster_lo())?;
25        write!(f, ", file_size = {}", self.file_size())?;
26        write!(f, ", is_end = {}", self.is_end())?;
27        write!(f, ", is_valid = {}", self.is_valid())?;
28        write!(f, ", is_lfn = {}", self.is_lfn())?;
29        write!(
30            f,
31            ", first_cluster_fat32 = {:?}",
32            self.first_cluster_fat32()
33        )?;
34        write!(
35            f,
36            ", first_cluster_fat16 = {:?}",
37            self.first_cluster_fat16()
38        )?;
39        write!(f, ">")?;
40        Ok(())
41    }
42}
43
44/// Represents the 32 byte directory entry. This is the same for FAT16 and
45/// FAT32 (except FAT16 doesn't use first_cluster_hi).
46impl<'a> OnDiskDirEntry<'a> {
47    pub(crate) const LEN: usize = 32;
48    pub(crate) const LEN_U32: u32 = 32;
49
50    define_field!(raw_attr, u8, 11);
51    define_field!(create_time, u16, 14);
52    define_field!(create_date, u16, 16);
53    define_field!(last_access_data, u16, 18);
54    define_field!(first_cluster_hi, u16, 20);
55    define_field!(write_time, u16, 22);
56    define_field!(write_date, u16, 24);
57    define_field!(first_cluster_lo, u16, 26);
58    define_field!(file_size, u32, 28);
59
60    /// Create a new on-disk directory entry from a block of 32 bytes read
61    /// from a directory file.
62    pub fn new(data: &[u8]) -> OnDiskDirEntry {
63        OnDiskDirEntry { data }
64    }
65
66    /// Is this the last entry in the directory?
67    pub fn is_end(&self) -> bool {
68        self.data[0] == 0x00
69    }
70
71    /// Is this a valid entry?
72    pub fn is_valid(&self) -> bool {
73        !self.is_end() && (self.data[0] != 0xE5)
74    }
75
76    /// Is this a Long Filename entry?
77    pub fn is_lfn(&self) -> bool {
78        let attributes = Attributes::create_from_fat(self.raw_attr());
79        attributes.is_lfn()
80    }
81
82    /// If this is an LFN, get the contents so we can re-assemble the filename.
83    pub fn lfn_contents(&self) -> Option<LfnEntry> {
84        if self.is_lfn() {
85            let mut buffer = [' '; 13];
86            let is_start = (self.data[0] & 0x40) != 0;
87            let sequence = self.data[0] & 0x1F;
88            let checksum = self.data[13];
89            // LFNs store UCS-2, so we can map from 16-bit char to 32-bit char without problem.
90            buffer[0] =
91                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[1..=2]))).unwrap();
92            buffer[1] =
93                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[3..=4]))).unwrap();
94            buffer[2] =
95                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[5..=6]))).unwrap();
96            buffer[3] =
97                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[7..=8]))).unwrap();
98            buffer[4] = core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[9..=10])))
99                .unwrap();
100            buffer[5] =
101                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[14..=15])))
102                    .unwrap();
103            buffer[6] =
104                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[16..=17])))
105                    .unwrap();
106            buffer[7] =
107                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[18..=19])))
108                    .unwrap();
109            buffer[8] =
110                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[20..=21])))
111                    .unwrap();
112            buffer[9] =
113                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[22..=23])))
114                    .unwrap();
115            buffer[10] =
116                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[24..=25])))
117                    .unwrap();
118            buffer[11] =
119                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[28..=29])))
120                    .unwrap();
121            buffer[12] =
122                core::char::from_u32(u32::from(LittleEndian::read_u16(&self.data[30..=31])))
123                    .unwrap();
124            Some(LfnEntry {
125                is_start,
126                sequence,
127                checksum,
128                buffer,
129            })
130        } else {
131            None
132        }
133    }
134
135    /// Does this on-disk entry match the given filename?
136    pub fn matches(&self, sfn: &ShortFileName) -> bool {
137        self.data[0..11] == sfn.contents
138    }
139
140    /// Which cluster, if any, does this file start at? Assumes this is from a FAT32 volume.
141    pub fn first_cluster_fat32(&self) -> ClusterId {
142        let cluster_no =
143            (u32::from(self.first_cluster_hi()) << 16) | u32::from(self.first_cluster_lo());
144        ClusterId(cluster_no)
145    }
146
147    /// Which cluster, if any, does this file start at? Assumes this is from a FAT16 volume.
148    fn first_cluster_fat16(&self) -> ClusterId {
149        let cluster_no = u32::from(self.first_cluster_lo());
150        ClusterId(cluster_no)
151    }
152
153    /// Convert the on-disk format into a DirEntry
154    pub fn get_entry(
155        &self,
156        fat_type: FatType,
157        entry_block: BlockIdx,
158        entry_offset: u32,
159    ) -> DirEntry {
160        let mut result = DirEntry {
161            name: ShortFileName {
162                contents: [0u8; 11],
163            },
164            mtime: Timestamp::from_fat(self.write_date(), self.write_time()),
165            ctime: Timestamp::from_fat(self.create_date(), self.create_time()),
166            attributes: Attributes::create_from_fat(self.raw_attr()),
167            cluster: if fat_type == FatType::Fat32 {
168                self.first_cluster_fat32()
169            } else {
170                self.first_cluster_fat16()
171            },
172            size: self.file_size(),
173            entry_block,
174            entry_offset,
175        };
176        result.name.contents.copy_from_slice(&self.data[0..11]);
177        result
178    }
179}