1use alloc::{format, string::String, string::ToString, vec::Vec};
7use core::mem;
8
9#[derive(Debug, Clone, Copy)]
14#[repr(C, packed)]
15pub struct Fat32BootSector {
16 pub jump_instruction: [u8; 3],
18 pub oem_name: [u8; 8],
20 pub bytes_per_sector: u16,
22 pub sectors_per_cluster: u8,
24 pub reserved_sectors: u16,
26 pub fat_count: u8,
28 pub max_root_entries: u16,
30 pub total_sectors_16: u16,
32 pub media_descriptor: u8,
34 pub sectors_per_fat_16: u16,
36 pub sectors_per_track: u16,
38 pub heads: u16,
40 pub hidden_sectors: u32,
42 pub total_sectors_32: u32,
44 pub sectors_per_fat: u32,
46 pub extended_flags: u16,
48 pub fs_version: u16,
50 pub root_cluster: u32,
52 pub fs_info_sector: u16,
54 pub backup_boot_sector: u16,
56 pub reserved: [u8; 12],
58 pub drive_number: u8,
60 pub reserved1: u8,
62 pub boot_signature: u8,
64 pub volume_serial: u32,
66 pub volume_label: [u8; 11],
68 pub fs_type: [u8; 8],
70 pub boot_code: [u8; 420],
72 pub signature: u16,
74}
75
76impl Fat32BootSector {
77 pub fn is_valid(&self) -> bool {
79 if self.signature != 0xAA55 {
81 return false;
82 }
83
84 match self.bytes_per_sector {
86 512 | 1024 | 2048 | 4096 => {}
87 _ => return false,
88 }
89
90 if self.sectors_per_cluster == 0
92 || (self.sectors_per_cluster & (self.sectors_per_cluster - 1)) != 0
93 {
94 return false;
95 }
96
97 if self.max_root_entries != 0 {
99 return false;
100 }
101
102 if self.sectors_per_fat_16 != 0 {
104 return false;
105 }
106
107 true
108 }
109
110 pub fn total_sectors(&self) -> u32 {
112 if self.total_sectors_16 != 0 {
113 self.total_sectors_16 as u32
114 } else {
115 self.total_sectors_32
116 }
117 }
118
119 pub fn first_data_sector(&self) -> u32 {
121 self.reserved_sectors as u32 + (self.fat_count as u32 * self.sectors_per_fat)
122 }
123
124 pub fn data_sectors(&self) -> u32 {
126 let total_sectors = self.total_sectors();
127 let first_data_sector = self.first_data_sector();
128 total_sectors - first_data_sector
129 }
130
131 pub fn cluster_count(&self) -> u32 {
133 self.data_sectors() / self.sectors_per_cluster as u32
134 }
135}
136
137#[derive(Debug, Clone, Copy)]
142#[repr(C, packed)]
143pub struct Fat32DirectoryEntry {
144 pub name: [u8; 11],
146 pub attributes: u8,
148 pub nt_reserved: u8,
150 pub creation_time_tenths: u8,
152 pub creation_time: u16,
154 pub creation_date: u16,
156 pub last_access_date: u16,
158 pub cluster_high: u16,
160 pub modification_time: u16,
162 pub modification_date: u16,
164 pub cluster_low: u16,
166 pub file_size: u32,
168}
169
170impl Fat32DirectoryEntry {
171 pub fn is_free(&self) -> bool {
173 self.name[0] == 0x00 || self.name[0] == 0xE5
174 }
175
176 pub fn is_last(&self) -> bool {
178 self.name[0] == 0x00
179 }
180
181 pub fn is_long_filename(&self) -> bool {
183 self.attributes == 0x0F
184 }
185
186 pub fn cluster(&self) -> u32 {
188 (self.cluster_high as u32) << 16 | (self.cluster_low as u32)
189 }
190
191 pub fn set_cluster(&mut self, cluster: u32) {
193 self.cluster_high = (cluster >> 16) as u16;
194 self.cluster_low = (cluster & 0xFFFF) as u16;
195 }
196
197 pub fn update_cluster_and_size(&mut self, cluster: u32, size: u32) {
199 self.set_cluster(cluster);
200 self.file_size = size;
201 }
202
203 pub fn is_directory(&self) -> bool {
205 self.attributes & 0x10 != 0
206 }
207
208 pub fn is_file(&self) -> bool {
210 !self.is_directory() && !self.is_volume_label() && !self.is_long_filename()
211 }
212
213 pub fn is_volume_label(&self) -> bool {
215 self.attributes & 0x08 != 0
216 }
217
218 pub fn filename(&self) -> String {
220 if self.name[0] == 0x05 {
222 let mut name = self.name;
224 name[0] = 0xE5;
225 return Self::parse_filename(&name);
226 }
227
228 Self::parse_filename(&self.name)
229 }
230
231 fn parse_filename(name: &[u8; 11]) -> alloc::string::String {
233 use alloc::string::String;
234
235 let mut result = String::new();
236
237 let mut main_name_len = 8;
239 for i in (0..8).rev() {
240 if name[i] != b' ' {
241 main_name_len = i + 1;
242 break;
243 }
244 }
245
246 if main_name_len == 0 {
247 return result;
248 }
249
250 for i in 0..main_name_len {
251 result.push((name[i] as char).to_ascii_lowercase());
253 }
254
255 let mut ext_len = 3;
257 for i in (8..11).rev() {
258 if name[i] != b' ' {
259 ext_len = i - 8 + 1;
260 break;
261 }
262 }
263
264 if ext_len > 0 && name[8] != b' ' {
265 result.push('.');
266 for i in 8..(8 + ext_len) {
267 result.push((name[i] as char).to_ascii_lowercase());
269 }
270 }
271
272 result
273 }
274
275 pub fn new_file(name: &str, cluster: u32, size: u32) -> Self {
277 let mut entry = Self {
278 name: [b' '; 11],
279 attributes: 0x00, nt_reserved: 0,
281 creation_time_tenths: 0,
282 creation_time: 0,
283 creation_date: 0,
284 last_access_date: 0,
285 cluster_high: (cluster >> 16) as u16,
286 modification_time: 0,
287 modification_date: 0,
288 cluster_low: (cluster & 0xFFFF) as u16,
289 file_size: size,
290 };
291
292 let sfn = Self::generate_sfn(name, 1);
293 entry.set_name(sfn);
294 entry
295 }
296
297 pub fn new_directory(name: &str, cluster: u32) -> Self {
299 let mut entry = Self {
300 name: [b' '; 11],
301 attributes: 0x10, nt_reserved: 0,
303 creation_time_tenths: 0,
304 creation_time: 0,
305 creation_date: 0,
306 last_access_date: 0,
307 cluster_high: (cluster >> 16) as u16,
308 modification_time: 0,
309 modification_date: 0,
310 cluster_low: (cluster & 0xFFFF) as u16,
311 file_size: 0, };
313
314 let sfn = Self::generate_sfn(name, 1);
315 entry.set_name(sfn);
316 entry
317 }
318
319 fn set_name(&mut self, name: [u8; 11]) {
321 self.name = name;
322 }
323
324 pub fn generate_sfn(name: &str, numeric_tail: u32) -> [u8; 11] {
326 let mut sfn = [b' '; 11];
327
328 if let Some(dot_pos) = name.rfind('.') {
330 let main_name = &name[..dot_pos];
331 let extension = &name[dot_pos + 1..];
332
333 let main_name_upper = main_name.to_ascii_uppercase();
335 let main_bytes: Vec<u8> = main_name_upper
336 .bytes()
337 .filter(|&b| Self::is_valid_sfn_char(b))
338 .collect();
339
340 let extension_upper = extension.to_ascii_uppercase();
342 let ext_bytes: Vec<u8> = extension_upper
343 .bytes()
344 .filter(|&b| Self::is_valid_sfn_char(b))
345 .collect();
346
347 let needs_numeric_tail = main_bytes.len() > 8
349 || ext_bytes.len() > 3
350 || main_name != main_name_upper
351 || extension != extension_upper;
352
353 if needs_numeric_tail {
354 let tail_str = format!("~{}", numeric_tail);
356 let available_chars = 8 - tail_str.len();
357
358 let copy_len = core::cmp::min(main_bytes.len(), available_chars);
360 for i in 0..copy_len {
361 sfn[i] = main_bytes[i];
362 }
363
364 for (i, byte) in tail_str.bytes().enumerate() {
366 if copy_len + i < 8 {
367 sfn[copy_len + i] = byte;
368 }
369 }
370 } else {
371 let copy_len = core::cmp::min(main_bytes.len(), 8);
373 for i in 0..copy_len {
374 sfn[i] = main_bytes[i];
375 }
376 }
377
378 let ext_len = core::cmp::min(ext_bytes.len(), 3);
380 for i in 0..ext_len {
381 sfn[8 + i] = ext_bytes[i];
382 }
383 } else {
384 let main_name_upper = name.to_ascii_uppercase();
386 let main_bytes: Vec<u8> = main_name_upper
387 .bytes()
388 .filter(|&b| Self::is_valid_sfn_char(b))
389 .collect();
390
391 let needs_numeric_tail = main_bytes.len() > 8 || name != main_name_upper;
393
394 if needs_numeric_tail {
395 let tail_str = format!("~{}", numeric_tail);
397 let available_chars = 8 - tail_str.len();
398
399 let copy_len = core::cmp::min(main_bytes.len(), available_chars);
401 for i in 0..copy_len {
402 sfn[i] = main_bytes[i];
403 }
404
405 for (i, byte) in tail_str.bytes().enumerate() {
407 if copy_len + i < 8 {
408 sfn[copy_len + i] = byte;
409 }
410 }
411 } else {
412 let copy_len = core::cmp::min(main_bytes.len(), 8);
414 for i in 0..copy_len {
415 sfn[i] = main_bytes[i];
416 }
417 }
418 }
419
420 sfn
421 }
422
423 fn is_valid_sfn_char(c: u8) -> bool {
425 match c {
426 b'"' | b'*' | b'+' | b',' | b'/' | b':' | b';' | b'<' | b'=' | b'>' | b'?' | b'['
428 | b'\\' | b']' | b'|' | b' ' => false,
429 0x00..=0x1F => false,
431 _ => true,
433 }
434 }
435}
436
437pub const FAT32_EOC: u32 = 0x0FFFFFF8; pub const FAT32_BAD: u32 = 0x0FFFFFF7; pub const FAT32_FREE: u32 = 0x00000000; pub const ATTR_READ_ONLY: u8 = 0x01;
444pub const ATTR_HIDDEN: u8 = 0x02;
445pub const ATTR_SYSTEM: u8 = 0x04;
446pub const ATTR_VOLUME_ID: u8 = 0x08;
447pub const ATTR_DIRECTORY: u8 = 0x10;
448pub const ATTR_ARCHIVE: u8 = 0x20;
449pub const ATTR_LONG_NAME: u8 = 0x0F;
450
451pub const DIR_ENTRY_SIZE: usize = mem::size_of::<Fat32DirectoryEntry>();
453
454const _: () = assert!(mem::size_of::<Fat32BootSector>() == 512);
456const _: () = assert!(mem::size_of::<Fat32DirectoryEntry>() == 32);
457const _: () = assert!(mem::size_of::<Fat32FsInfo>() == 512);
458
459#[derive(Debug, Clone)]
470pub struct Fat32DirectoryEntryInternal {
471 pub filename: String,
473 pub short_filename: String,
475 pub attributes: u8,
477 pub start_cluster: u32,
479 pub file_size: u32,
481 pub creation_time: FileTime,
483 pub modification_time: FileTime,
485 pub last_access_date: u16,
487}
488
489#[derive(Debug, Clone, Copy)]
491pub struct FileTime {
492 pub time: u16,
494 pub date: u16,
496 pub tenths: u8,
498}
499
500impl Fat32DirectoryEntryInternal {
501 pub fn from_raw_entry(raw_entry: &Fat32DirectoryEntry) -> Self {
503 let short_filename = Self::parse_sfn(&raw_entry.name);
504
505 Self {
506 filename: short_filename.clone(), short_filename,
508 attributes: raw_entry.attributes,
509 start_cluster: raw_entry.cluster(),
510 file_size: raw_entry.file_size,
511 creation_time: FileTime {
512 time: raw_entry.creation_time,
513 date: raw_entry.creation_date,
514 tenths: raw_entry.creation_time_tenths,
515 },
516 modification_time: FileTime {
517 time: raw_entry.modification_time,
518 date: raw_entry.modification_date,
519 tenths: 0,
520 },
521 last_access_date: raw_entry.last_access_date,
522 }
523 }
524
525 pub fn from_raw(raw_entry: Fat32DirectoryEntry) -> Self {
527 Self::from_raw_entry(&raw_entry)
528 }
529
530 pub fn name(&self) -> String {
532 self.filename.clone()
533 }
534
535 pub fn cluster(&self) -> u32 {
537 self.start_cluster
538 }
539
540 pub fn size(&self) -> u32 {
542 self.file_size
543 }
544
545 pub fn set_long_filename(&mut self, lfn: String) {
547 self.filename = lfn;
548 }
549
550 pub fn is_directory(&self) -> bool {
552 (self.attributes & 0x10) != 0
553 }
554
555 pub fn is_file(&self) -> bool {
557 !self.is_directory() && (self.attributes & 0x08) == 0
558 }
559
560 pub fn is_hidden(&self) -> bool {
562 (self.attributes & 0x02) != 0
563 }
564
565 pub fn is_read_only(&self) -> bool {
567 (self.attributes & 0x01) != 0
568 }
569
570 fn parse_sfn(name: &[u8; 11]) -> String {
572 let mut result = String::new();
573
574 let mut main_name_len = 8;
576 for i in (0..8).rev() {
577 if name[i] != b' ' {
578 main_name_len = i + 1;
579 break;
580 }
581 }
582
583 if main_name_len == 0 {
584 return result;
585 }
586
587 for i in 0..main_name_len {
588 result.push((name[i] as char).to_ascii_lowercase());
589 }
590
591 let mut ext_len = 3;
593 for i in (8..11).rev() {
594 if name[i] != b' ' {
595 ext_len = i - 8 + 1;
596 break;
597 }
598 }
599
600 if ext_len > 0 && name[8] != b' ' {
601 result.push('.');
602 for i in 8..(8 + ext_len) {
603 result.push((name[i] as char).to_ascii_lowercase());
604 }
605 }
606
607 result
608 }
609
610 pub fn to_raw_entry(&self) -> Fat32DirectoryEntry {
612 let mut raw_entry = Fat32DirectoryEntry {
613 name: [b' '; 11],
614 attributes: self.attributes,
615 nt_reserved: 0,
616 creation_time_tenths: self.creation_time.tenths,
617 creation_time: self.creation_time.time,
618 creation_date: self.creation_time.date,
619 last_access_date: self.last_access_date,
620 cluster_high: (self.start_cluster >> 16) as u16,
621 modification_time: self.modification_time.time,
622 modification_date: self.modification_time.date,
623 cluster_low: (self.start_cluster & 0xFFFF) as u16,
624 file_size: self.file_size,
625 };
626
627 let sfn = Fat32DirectoryEntry::generate_sfn(&self.filename, 1);
629 raw_entry.set_name(sfn);
630 raw_entry
631 }
632}
633
634#[derive(Debug, Clone, Copy)]
636#[repr(C, packed)]
637pub struct Fat32FsInfo {
638 pub lead_signature: u32,
640 pub reserved1: [u8; 480],
642 pub structure_signature: u32,
644 pub free_cluster_count: u32,
646 pub next_free_cluster: u32,
648 pub reserved2: [u8; 12],
650 pub trail_signature: u32,
652}
653
654impl Fat32FsInfo {
655 pub fn is_valid(&self) -> bool {
657 self.lead_signature == 0x41615252
658 && self.structure_signature == 0x61417272
659 && self.trail_signature == 0xAA550000
660 }
661}
662
663#[derive(Debug, Clone, Copy)]
665#[repr(C, packed)]
666pub struct Fat32LFNEntry {
667 pub sequence: u8,
669 pub name1: [u16; 5],
671 pub attributes: u8,
673 pub entry_type: u8,
675 pub checksum: u8,
677 pub name2: [u16; 6],
679 pub cluster: u16,
681 pub name3: [u16; 2],
683}
684
685impl Fat32LFNEntry {
686 pub fn is_lfn(&self) -> bool {
688 self.attributes == 0x0F
689 }
690
691 pub fn is_last_lfn(&self) -> bool {
693 (self.sequence & 0x40) != 0
694 }
695
696 pub fn sequence_number(&self) -> u8 {
698 self.sequence & 0x3F
699 }
700
701 pub fn extract_chars(&self) -> Vec<u16> {
703 let mut chars = Vec::new();
704
705 let name1_copy = self.name1;
707 for ch in name1_copy {
708 if ch != 0 && ch != 0xFFFF {
709 chars.push(ch);
710 }
711 }
712
713 let name2_copy = self.name2;
715 for ch in name2_copy {
716 if ch != 0 && ch != 0xFFFF {
717 chars.push(ch);
718 }
719 }
720
721 let name3_copy = self.name3;
723 for ch in name3_copy {
724 if ch != 0 && ch != 0xFFFF {
725 chars.push(ch);
726 }
727 }
728
729 chars
730 }
731}
732
733pub struct Fat32DirectoryEntryBuilder {
735 entry: Fat32DirectoryEntryInternal,
736}
737
738impl Fat32DirectoryEntryBuilder {
739 pub fn new_file(filename: &str, cluster: u32, size: u32) -> Self {
741 let short_filename = Self::generate_short_filename(filename);
742
743 Self {
744 entry: Fat32DirectoryEntryInternal {
745 filename: filename.to_string(),
746 short_filename,
747 attributes: 0, start_cluster: cluster,
749 file_size: size,
750 creation_time: FileTime {
751 time: 0,
752 date: 0,
753 tenths: 0,
754 },
755 modification_time: FileTime {
756 time: 0,
757 date: 0,
758 tenths: 0,
759 },
760 last_access_date: 0,
761 },
762 }
763 }
764
765 pub fn new_directory(dirname: &str, cluster: u32) -> Self {
767 let short_filename = Self::generate_short_filename(dirname);
768
769 Self {
770 entry: Fat32DirectoryEntryInternal {
771 filename: dirname.to_string(),
772 short_filename,
773 attributes: 0x10, start_cluster: cluster,
775 file_size: 0, creation_time: FileTime {
777 time: 0,
778 date: 0,
779 tenths: 0,
780 },
781 modification_time: FileTime {
782 time: 0,
783 date: 0,
784 tenths: 0,
785 },
786 last_access_date: 0,
787 },
788 }
789 }
790
791 pub fn attributes(mut self, attrs: u8) -> Self {
793 self.entry.attributes |= attrs;
794 self
795 }
796
797 pub fn build(self) -> Fat32DirectoryEntryInternal {
799 self.entry
800 }
801
802 fn generate_short_filename(filename: &str) -> String {
804 if let Some(dot_pos) = filename.rfind('.') {
806 let name_part = &filename[..dot_pos];
807 let ext_part = &filename[dot_pos + 1..];
808
809 let short_name = if name_part.len() <= 8 {
810 name_part.to_string()
811 } else {
812 format!("{:.6}~1", name_part)
813 };
814
815 let short_ext = if ext_part.len() <= 3 {
816 ext_part.to_string()
817 } else {
818 ext_part[..3].to_string()
819 };
820
821 format!("{}.{}", short_name, short_ext).to_uppercase()
822 } else {
823 if filename.len() <= 8 {
825 filename.to_uppercase()
826 } else {
827 format!("{:.6}~1", filename).to_uppercase()
828 }
829 }
830 }
831}