⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/fs/
fat32.rs

1//! FAT32 Filesystem Implementation
2//!
3//! Read/write support for the FAT32 filesystem format.
4//! Supports 8.3 short names and VFAT long file names (LFN).
5//! Integrates with block devices (VirtIO-blk, NVMe, etc.) via the BlockDevice
6//! trait.
7//!
8//! Struct fields, attribute constants, and helper methods define the complete
9//! FAT32 on-disk format. Unused items are retained for format completeness.
10#![allow(dead_code)]
11
12use alloc::{collections::BTreeMap, string::String, sync::Arc, vec, vec::Vec};
13use core::sync::atomic::{AtomicU64, Ordering};
14
15#[cfg(not(target_arch = "aarch64"))]
16use spin::RwLock;
17
18#[cfg(target_arch = "aarch64")]
19use super::bare_lock::RwLock;
20use super::{DirEntry, Filesystem, Metadata, NodeType, Permissions, VfsNode};
21use crate::error::{FsError, KernelError};
22
23/// FAT32 end-of-chain marker
24const FAT32_EOC: u32 = 0x0FFF_FFF8;
25
26/// FAT32 free cluster marker
27const FAT32_FREE: u32 = 0x0000_0000;
28
29/// FAT32 directory entry size
30const DIR_ENTRY_SIZE: usize = 32;
31
32/// Attribute flags for directory entries
33const ATTR_READ_ONLY: u8 = 0x01;
34const ATTR_HIDDEN: u8 = 0x02;
35const ATTR_SYSTEM: u8 = 0x04;
36const ATTR_DIRECTORY: u8 = 0x10;
37const ATTR_LONG_NAME: u8 = 0x0F;
38
39/// BIOS Parameter Block (BPB) -- parsed from boot sector
40#[derive(Debug, Clone)]
41pub struct Bpb {
42    pub bytes_per_sector: u16,
43    pub sectors_per_cluster: u8,
44    pub reserved_sectors: u16,
45    pub num_fats: u8,
46    pub total_sectors_32: u32,
47    pub fat_size_32: u32,
48    pub root_cluster: u32,
49    pub fs_info_sector: u16,
50}
51
52impl Bpb {
53    /// Parse BPB from boot sector bytes (first 512 bytes)
54    pub fn parse(sector: &[u8]) -> Result<Self, KernelError> {
55        if sector.len() < 512 {
56            return Err(KernelError::FsError(FsError::CorruptedData));
57        }
58
59        // Check boot signature
60        if sector[510] != 0x55 || sector[511] != 0xAA {
61            return Err(KernelError::FsError(FsError::CorruptedData));
62        }
63
64        let bytes_per_sector = u16::from_le_bytes([sector[11], sector[12]]);
65        let sectors_per_cluster = sector[13];
66        let reserved_sectors = u16::from_le_bytes([sector[14], sector[15]]);
67        let num_fats = sector[16];
68        let total_sectors_32 = u32::from_le_bytes([sector[32], sector[33], sector[34], sector[35]]);
69        let fat_size_32 = u32::from_le_bytes([sector[36], sector[37], sector[38], sector[39]]);
70        let root_cluster = u32::from_le_bytes([sector[44], sector[45], sector[46], sector[47]]);
71        let fs_info_sector = u16::from_le_bytes([sector[48], sector[49]]);
72
73        if bytes_per_sector == 0 || sectors_per_cluster == 0 || num_fats == 0 {
74            return Err(KernelError::FsError(FsError::CorruptedData));
75        }
76
77        Ok(Self {
78            bytes_per_sector,
79            sectors_per_cluster,
80            reserved_sectors,
81            num_fats,
82            total_sectors_32,
83            fat_size_32,
84            root_cluster,
85            fs_info_sector,
86        })
87    }
88
89    /// First sector of the data region
90    fn data_start_sector(&self) -> u32 {
91        self.reserved_sectors as u32 + (self.num_fats as u32 * self.fat_size_32)
92    }
93
94    /// Convert cluster number to absolute sector number
95    fn cluster_to_sector(&self, cluster: u32) -> u32 {
96        self.data_start_sector() + (cluster - 2) * self.sectors_per_cluster as u32
97    }
98
99    /// Bytes per cluster
100    fn cluster_size(&self) -> usize {
101        self.bytes_per_sector as usize * self.sectors_per_cluster as usize
102    }
103
104    /// Sector offset of a FAT entry for a given cluster
105    fn fat_sector_for_cluster(&self, cluster: u32) -> u32 {
106        let fat_offset = cluster * 4;
107        self.reserved_sectors as u32 + fat_offset / self.bytes_per_sector as u32
108    }
109
110    /// Byte offset within FAT sector for a given cluster
111    fn fat_offset_in_sector(&self, cluster: u32) -> usize {
112        ((cluster * 4) % self.bytes_per_sector as u32) as usize
113    }
114}
115
116/// In-memory FAT table cache
117struct FatTable {
118    /// FAT entries indexed by cluster number
119    entries: Vec<u32>,
120}
121
122impl FatTable {
123    fn new(num_clusters: usize) -> Self {
124        Self {
125            entries: vec![0u32; num_clusters],
126        }
127    }
128
129    fn get(&self, cluster: u32) -> u32 {
130        if (cluster as usize) < self.entries.len() {
131            self.entries[cluster as usize] & 0x0FFF_FFFF
132        } else {
133            FAT32_EOC
134        }
135    }
136
137    fn set(&mut self, cluster: u32, value: u32) {
138        if (cluster as usize) < self.entries.len() {
139            self.entries[cluster as usize] = value & 0x0FFF_FFFF;
140        }
141    }
142
143    /// Find a free cluster starting from `hint`
144    fn alloc_cluster(&mut self, hint: u32) -> Option<u32> {
145        let start = core::cmp::max(hint as usize, 2);
146        // Search from hint to end
147        for i in start..self.entries.len() {
148            if self.entries[i] == FAT32_FREE {
149                self.entries[i] = FAT32_EOC;
150                return Some(i as u32);
151            }
152        }
153        // Wrap around: search from 2 to hint
154        for i in 2..start {
155            if self.entries[i] == FAT32_FREE {
156                self.entries[i] = FAT32_EOC;
157                return Some(i as u32);
158            }
159        }
160        None
161    }
162
163    /// Free a cluster chain starting from `start`
164    fn free_chain(&mut self, start: u32) {
165        let mut current = start;
166        while (2..FAT32_EOC).contains(&current) {
167            let next = self.get(current);
168            self.entries[current as usize] = FAT32_FREE;
169            if next >= FAT32_EOC {
170                break;
171            }
172            current = next;
173        }
174    }
175
176    /// Get the chain of clusters starting from `start`
177    fn get_chain(&self, start: u32) -> Vec<u32> {
178        let mut chain = Vec::new();
179        let mut current = start;
180        while (2..FAT32_EOC).contains(&current) {
181            chain.push(current);
182            let next = self.get(current);
183            if next >= FAT32_EOC || chain.len() > 1_000_000 {
184                break;
185            }
186            current = next;
187        }
188        chain
189    }
190}
191
192/// Raw FAT32 directory entry (32 bytes)
193#[derive(Debug, Clone, Copy)]
194#[repr(C, packed)]
195struct RawDirEntry {
196    name: [u8; 11],
197    attr: u8,
198    _nt_reserved: u8,
199    create_time_tenth: u8,
200    create_time: u16,
201    create_date: u16,
202    access_date: u16,
203    first_cluster_hi: u16,
204    write_time: u16,
205    write_date: u16,
206    first_cluster_lo: u16,
207    file_size: u32,
208}
209
210impl RawDirEntry {
211    fn first_cluster(&self) -> u32 {
212        ((self.first_cluster_hi as u32) << 16) | self.first_cluster_lo as u32
213    }
214
215    fn is_free(&self) -> bool {
216        self.name[0] == 0xE5 || self.name[0] == 0x00
217    }
218
219    fn is_end(&self) -> bool {
220        self.name[0] == 0x00
221    }
222
223    fn is_long_name(&self) -> bool {
224        self.attr == ATTR_LONG_NAME
225    }
226
227    fn is_directory(&self) -> bool {
228        (self.attr & ATTR_DIRECTORY) != 0
229    }
230
231    fn short_name(&self) -> String {
232        let name_part: Vec<u8> = self.name[..8]
233            .iter()
234            .copied()
235            .take_while(|&b| b != b' ')
236            .collect();
237        let ext_part: Vec<u8> = self.name[8..11]
238            .iter()
239            .copied()
240            .take_while(|&b| b != b' ')
241            .collect();
242
243        let mut result = String::new();
244        for &b in &name_part {
245            result.push(b as char);
246        }
247        if !ext_part.is_empty() {
248            result.push('.');
249            for &b in &ext_part {
250                result.push(b as char);
251            }
252        }
253        result
254    }
255}
256
257/// Long file name directory entry
258#[derive(Debug, Clone, Copy)]
259#[repr(C, packed)]
260struct LfnDirEntry {
261    order: u8,
262    name1: [u16; 5],
263    attr: u8,
264    entry_type: u8,
265    checksum: u8,
266    name2: [u16; 6],
267    _first_cluster_lo: u16,
268    name3: [u16; 2],
269}
270
271impl LfnDirEntry {
272    /// Extract the Unicode characters from this LFN entry.
273    /// Uses read_unaligned to handle packed struct field access safely.
274    fn chars(&self) -> Vec<u16> {
275        let mut chars = Vec::new();
276
277        // Copy fields from packed struct to local arrays to avoid unaligned access
278        // SAFETY: addr_of! on packed fields avoids creating unaligned refs;
279        // read_unaligned handles alignment.
280        let name1: [u16; 5] = unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(self.name1)) };
281        let name2: [u16; 6] = unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(self.name2)) };
282        let name3: [u16; 2] = unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(self.name3)) };
283
284        for &c in &name1 {
285            if c == 0x0000 || c == 0xFFFF {
286                return chars;
287            }
288            chars.push(c);
289        }
290        for &c in &name2 {
291            if c == 0x0000 || c == 0xFFFF {
292                return chars;
293            }
294            chars.push(c);
295        }
296        for &c in &name3 {
297            if c == 0x0000 || c == 0xFFFF {
298                return chars;
299            }
300            chars.push(c);
301        }
302        chars
303    }
304}
305
306/// Global inode counter for FAT32 nodes
307static FAT32_NEXT_INODE: AtomicU64 = AtomicU64::new(1);
308
309/// In-memory representation of a FAT32 node (file or directory)
310struct Fat32Node {
311    node_type: NodeType,
312    /// Data cache for files; directory entry bytes for directories
313    data: RwLock<Vec<u8>>,
314    /// Children (populated on first readdir/lookup for directories)
315    children: RwLock<BTreeMap<String, Arc<Fat32Node>>>,
316    metadata: RwLock<Metadata>,
317    inode: u64,
318    parent_inode: u64,
319    /// Starting cluster on disk
320    start_cluster: RwLock<u32>,
321    /// Shared reference to filesystem state for cluster I/O
322    fs_state: Arc<RwLock<Fat32State>>,
323}
324
325/// Shared mutable state for the FAT32 filesystem
326struct Fat32State {
327    bpb: Bpb,
328    fat: FatTable,
329    /// Cached disk data: maps sector number -> sector bytes
330    sector_cache: BTreeMap<u64, Vec<u8>>,
331    /// Whether the filesystem has been modified (dirty)
332    dirty: bool,
333    /// Next free cluster hint
334    next_free_hint: u32,
335}
336
337impl Fat32State {
338    /// Read a cluster's data from cache or synthesize zeros
339    fn read_cluster(&self, cluster: u32) -> Vec<u8> {
340        let sector = self.bpb.cluster_to_sector(cluster);
341        let cluster_size = self.bpb.cluster_size();
342        let mut data = vec![0u8; cluster_size];
343
344        let sectors_per_cluster = self.bpb.sectors_per_cluster as u32;
345        for i in 0..sectors_per_cluster {
346            let sec = (sector + i) as u64;
347            if let Some(cached) = self.sector_cache.get(&sec) {
348                let offset = i as usize * self.bpb.bytes_per_sector as usize;
349                let len = core::cmp::min(cached.len(), self.bpb.bytes_per_sector as usize);
350                data[offset..offset + len].copy_from_slice(&cached[..len]);
351            }
352        }
353        data
354    }
355
356    /// Write a cluster's data to cache
357    fn write_cluster(&mut self, cluster: u32, data: &[u8]) {
358        let sector = self.bpb.cluster_to_sector(cluster);
359        let bps = self.bpb.bytes_per_sector as usize;
360        let sectors_per_cluster = self.bpb.sectors_per_cluster as u32;
361
362        for i in 0..sectors_per_cluster {
363            let sec = (sector + i) as u64;
364            let offset = i as usize * bps;
365            let end = core::cmp::min(offset + bps, data.len());
366            if offset < data.len() {
367                self.sector_cache.insert(sec, data[offset..end].to_vec());
368            }
369        }
370        self.dirty = true;
371    }
372
373    /// Read file data from a cluster chain
374    fn read_chain_data(&self, start_cluster: u32, size: usize) -> Vec<u8> {
375        let chain = self.fat.get_chain(start_cluster);
376        let mut data = Vec::with_capacity(size);
377
378        for &cluster in &chain {
379            let cluster_data = self.read_cluster(cluster);
380            let remaining = size.saturating_sub(data.len());
381            let take = core::cmp::min(remaining, cluster_data.len());
382            data.extend_from_slice(&cluster_data[..take]);
383            if data.len() >= size {
384                break;
385            }
386        }
387        data
388    }
389
390    /// Allocate clusters and write data
391    fn write_data(&mut self, start_cluster: u32, data: &[u8]) -> Result<u32, KernelError> {
392        let cluster_size = self.bpb.cluster_size();
393        let clusters_needed = if data.is_empty() {
394            0
395        } else {
396            data.len().div_ceil(cluster_size)
397        };
398
399        // Free old chain if any
400        if start_cluster >= 2 {
401            self.fat.free_chain(start_cluster);
402        }
403
404        if clusters_needed == 0 {
405            return Ok(0);
406        }
407
408        // Allocate new chain
409        let mut chain = Vec::with_capacity(clusters_needed);
410        for _ in 0..clusters_needed {
411            let cluster = self
412                .fat
413                .alloc_cluster(self.next_free_hint)
414                .ok_or(KernelError::FsError(FsError::NoSpace))?;
415            self.next_free_hint = cluster + 1;
416            chain.push(cluster);
417        }
418
419        // Link chain
420        for i in 0..chain.len() - 1 {
421            self.fat.set(chain[i], chain[i + 1]);
422        }
423        // Last cluster is already marked EOC by alloc_cluster
424
425        // Write data to clusters
426        for (i, &cluster) in chain.iter().enumerate() {
427            let offset = i * cluster_size;
428            let end = core::cmp::min(offset + cluster_size, data.len());
429            let mut cluster_buf = vec![0u8; cluster_size];
430            cluster_buf[..end - offset].copy_from_slice(&data[offset..end]);
431            self.write_cluster(cluster, &cluster_buf);
432        }
433
434        Ok(chain[0])
435    }
436
437    /// Parse directory entries from raw cluster data
438    fn parse_dir_entries(&self, dir_data: &[u8]) -> Vec<(String, u32, u32, u8)> {
439        let mut entries = Vec::new();
440        let mut lfn_parts: Vec<(u8, Vec<u16>)> = Vec::new();
441        let entry_count = dir_data.len() / DIR_ENTRY_SIZE;
442
443        for i in 0..entry_count {
444            let offset = i * DIR_ENTRY_SIZE;
445            let entry_bytes = &dir_data[offset..offset + DIR_ENTRY_SIZE];
446
447            if entry_bytes[0] == 0x00 {
448                break; // End of directory
449            }
450            if entry_bytes[0] == 0xE5 {
451                lfn_parts.clear();
452                continue; // Deleted entry
453            }
454
455            let attr = entry_bytes[11];
456
457            if attr == ATTR_LONG_NAME {
458                // LFN entry
459                // SAFETY: entry_bytes is a 32-byte aligned slice matching LfnDirEntry layout.
460                let lfn = unsafe { &*(entry_bytes.as_ptr() as *const LfnDirEntry) };
461                let order = lfn.order & 0x3F;
462                lfn_parts.push((order, lfn.chars()));
463                continue;
464            }
465
466            // Short name entry
467            // SAFETY: entry_bytes is a 32-byte aligned slice matching RawDirEntry layout.
468            let raw = unsafe { &*(entry_bytes.as_ptr() as *const RawDirEntry) };
469
470            // Build long name if LFN parts exist
471            let name = if !lfn_parts.is_empty() {
472                lfn_parts.sort_by_key(|(order, _)| *order);
473                let mut chars: Vec<u16> = Vec::new();
474                for (_, part) in &lfn_parts {
475                    chars.extend_from_slice(part);
476                }
477                lfn_parts.clear();
478
479                // Convert UTF-16 to ASCII/UTF-8
480                let mut name = String::new();
481                for &c in &chars {
482                    if c == 0 {
483                        break;
484                    }
485                    if c < 128 {
486                        name.push(c as u8 as char);
487                    } else {
488                        name.push('?'); // Non-ASCII replacement
489                    }
490                }
491                name
492            } else {
493                lfn_parts.clear();
494                raw.short_name()
495            };
496
497            // Skip . and .. entries
498            if name == "." || name == ".." {
499                continue;
500            }
501
502            entries.push((name, raw.first_cluster(), raw.file_size, raw.attr));
503        }
504
505        entries
506    }
507}
508
509impl Fat32Node {
510    fn new_file(
511        inode: u64,
512        parent_inode: u64,
513        start_cluster: u32,
514        size: usize,
515        fs_state: Arc<RwLock<Fat32State>>,
516    ) -> Self {
517        Self {
518            node_type: NodeType::File,
519            data: RwLock::new(Vec::new()),
520            children: RwLock::new(BTreeMap::new()),
521            metadata: RwLock::new(Metadata {
522                node_type: NodeType::File,
523                size,
524                permissions: Permissions::default(),
525                uid: 0,
526                gid: 0,
527                created: 0,
528                modified: 0,
529                accessed: 0,
530                inode,
531            }),
532            inode,
533            parent_inode,
534            start_cluster: RwLock::new(start_cluster),
535            fs_state,
536        }
537    }
538
539    fn new_directory(
540        inode: u64,
541        parent_inode: u64,
542        start_cluster: u32,
543        fs_state: Arc<RwLock<Fat32State>>,
544    ) -> Self {
545        Self {
546            node_type: NodeType::Directory,
547            data: RwLock::new(Vec::new()),
548            children: RwLock::new(BTreeMap::new()),
549            metadata: RwLock::new(Metadata {
550                node_type: NodeType::Directory,
551                size: 0,
552                permissions: Permissions::default(),
553                uid: 0,
554                gid: 0,
555                created: 0,
556                modified: 0,
557                accessed: 0,
558                inode,
559            }),
560            inode,
561            parent_inode,
562            start_cluster: RwLock::new(start_cluster),
563            fs_state,
564        }
565    }
566
567    /// Load directory children from disk if not already cached
568    fn ensure_children_loaded(&self) {
569        if self.node_type != NodeType::Directory {
570            return;
571        }
572
573        {
574            let children = self.children.read();
575            // Already loaded check: if we have any children, skip
576            // (empty directory is re-checked each time, which is acceptable)
577            if !children.is_empty() {
578                return;
579            }
580        }
581
582        let start = *self.start_cluster.read();
583        if start < 2 {
584            return;
585        }
586
587        let parsed = {
588            let state = self.fs_state.read();
589            let dir_data = state.read_chain_data(start, 1024 * 1024); // Up to 1MB directory
590            state.parse_dir_entries(&dir_data)
591        };
592
593        let mut children = self.children.write();
594        for (name, cluster, size, attr) in parsed {
595            let inode = FAT32_NEXT_INODE.fetch_add(1, Ordering::Relaxed);
596            let node = if (attr & ATTR_DIRECTORY) != 0 {
597                Arc::new(Fat32Node::new_directory(
598                    inode,
599                    self.inode,
600                    cluster,
601                    self.fs_state.clone(),
602                ))
603            } else {
604                Arc::new(Fat32Node::new_file(
605                    inode,
606                    self.inode,
607                    cluster,
608                    size as usize,
609                    self.fs_state.clone(),
610                ))
611            };
612            children.insert(name, node);
613        }
614    }
615}
616
617impl VfsNode for Fat32Node {
618    fn node_type(&self) -> NodeType {
619        self.node_type
620    }
621
622    fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<usize, KernelError> {
623        if self.node_type != NodeType::File {
624            return Err(KernelError::FsError(FsError::NotAFile));
625        }
626
627        let meta = self.metadata.read();
628        if offset >= meta.size {
629            return Ok(0);
630        }
631
632        let start = *self.start_cluster.read();
633        let file_data = {
634            let state = self.fs_state.read();
635            state.read_chain_data(start, meta.size)
636        };
637
638        let bytes_to_read = core::cmp::min(buffer.len(), file_data.len().saturating_sub(offset));
639        buffer[..bytes_to_read].copy_from_slice(&file_data[offset..offset + bytes_to_read]);
640
641        Ok(bytes_to_read)
642    }
643
644    fn write(&self, offset: usize, data: &[u8]) -> Result<usize, KernelError> {
645        if self.node_type != NodeType::File {
646            return Err(KernelError::FsError(FsError::NotAFile));
647        }
648
649        // Read existing data
650        let old_start = *self.start_cluster.read();
651        let old_size = self.metadata.read().size;
652
653        let mut file_data = {
654            let state = self.fs_state.read();
655            if old_start >= 2 && old_size > 0 {
656                state.read_chain_data(old_start, old_size)
657            } else {
658                Vec::new()
659            }
660        };
661
662        // Extend if needed
663        if offset > file_data.len() {
664            file_data.resize(offset, 0);
665        }
666        let new_end = offset + data.len();
667        if new_end > file_data.len() {
668            file_data.resize(new_end, 0);
669        }
670        file_data[offset..new_end].copy_from_slice(data);
671
672        // Write back
673        let new_cluster = {
674            let mut state = self.fs_state.write();
675            state.write_data(old_start, &file_data)?
676        };
677
678        *self.start_cluster.write() = new_cluster;
679        let mut meta = self.metadata.write();
680        meta.size = file_data.len();
681        meta.modified = crate::arch::timer::get_timestamp_secs();
682
683        Ok(data.len())
684    }
685
686    fn metadata(&self) -> Result<Metadata, KernelError> {
687        Ok(self.metadata.read().clone())
688    }
689
690    fn readdir(&self) -> Result<Vec<DirEntry>, KernelError> {
691        if self.node_type != NodeType::Directory {
692            return Err(KernelError::FsError(FsError::NotADirectory));
693        }
694
695        self.ensure_children_loaded();
696
697        let children = self.children.read();
698        let mut entries = Vec::new();
699
700        entries.push(DirEntry {
701            name: String::from("."),
702            node_type: NodeType::Directory,
703            inode: self.inode,
704        });
705        entries.push(DirEntry {
706            name: String::from(".."),
707            node_type: NodeType::Directory,
708            inode: self.parent_inode,
709        });
710
711        for (name, child) in children.iter() {
712            entries.push(DirEntry {
713                name: name.clone(),
714                node_type: child.node_type,
715                inode: child.inode,
716            });
717        }
718
719        Ok(entries)
720    }
721
722    fn lookup(&self, name: &str) -> Result<Arc<dyn VfsNode>, KernelError> {
723        if self.node_type != NodeType::Directory {
724            return Err(KernelError::FsError(FsError::NotADirectory));
725        }
726
727        self.ensure_children_loaded();
728
729        let children = self.children.read();
730        children
731            .get(name)
732            .map(|node| node.clone() as Arc<dyn VfsNode>)
733            .ok_or(KernelError::FsError(FsError::NotFound))
734    }
735
736    fn create(
737        &self,
738        name: &str,
739        permissions: Permissions,
740    ) -> Result<Arc<dyn VfsNode>, KernelError> {
741        if self.node_type != NodeType::Directory {
742            return Err(KernelError::FsError(FsError::NotADirectory));
743        }
744
745        self.ensure_children_loaded();
746
747        let mut children = self.children.write();
748        if children.contains_key(name) {
749            return Err(KernelError::FsError(FsError::AlreadyExists));
750        }
751
752        let inode = FAT32_NEXT_INODE.fetch_add(1, Ordering::Relaxed);
753        let new_file = Arc::new(Fat32Node::new_file(
754            inode,
755            self.inode,
756            0, // No clusters allocated yet
757            0,
758            self.fs_state.clone(),
759        ));
760        // Override permissions
761        new_file.metadata.write().permissions = permissions;
762        children.insert(String::from(name), new_file.clone());
763
764        Ok(new_file as Arc<dyn VfsNode>)
765    }
766
767    fn mkdir(&self, name: &str, permissions: Permissions) -> Result<Arc<dyn VfsNode>, KernelError> {
768        if self.node_type != NodeType::Directory {
769            return Err(KernelError::FsError(FsError::NotADirectory));
770        }
771
772        self.ensure_children_loaded();
773
774        let mut children = self.children.write();
775        if children.contains_key(name) {
776            return Err(KernelError::FsError(FsError::AlreadyExists));
777        }
778
779        // Allocate a cluster for the new directory
780        let cluster = {
781            let mut state = self.fs_state.write();
782            let hint = state.next_free_hint;
783            let cluster = state
784                .fat
785                .alloc_cluster(hint)
786                .ok_or(KernelError::FsError(FsError::NoSpace))?;
787            state.next_free_hint = cluster + 1;
788
789            // Write empty directory data (. and .. entries)
790            let cluster_size = state.bpb.cluster_size();
791            let empty = vec![0u8; cluster_size];
792            state.write_cluster(cluster, &empty);
793            state.dirty = true;
794            cluster
795        };
796
797        let inode = FAT32_NEXT_INODE.fetch_add(1, Ordering::Relaxed);
798        let new_dir = Arc::new(Fat32Node::new_directory(
799            inode,
800            self.inode,
801            cluster,
802            self.fs_state.clone(),
803        ));
804        new_dir.metadata.write().permissions = permissions;
805        children.insert(String::from(name), new_dir.clone());
806
807        Ok(new_dir as Arc<dyn VfsNode>)
808    }
809
810    fn unlink(&self, name: &str) -> Result<(), KernelError> {
811        if self.node_type != NodeType::Directory {
812            return Err(KernelError::FsError(FsError::NotADirectory));
813        }
814
815        self.ensure_children_loaded();
816
817        let mut children = self.children.write();
818
819        if let Some(node) = children.get(name) {
820            if node.node_type == NodeType::Directory {
821                let node_children = node.children.read();
822                if !node_children.is_empty() {
823                    return Err(KernelError::FsError(FsError::DirectoryNotEmpty));
824                }
825            }
826
827            // Free cluster chain
828            let start = *node.start_cluster.read();
829            if start >= 2 {
830                let mut state = self.fs_state.write();
831                state.fat.free_chain(start);
832                state.dirty = true;
833            }
834
835            children.remove(name);
836            Ok(())
837        } else {
838            Err(KernelError::FsError(FsError::NotFound))
839        }
840    }
841
842    fn truncate(&self, size: usize) -> Result<(), KernelError> {
843        if self.node_type != NodeType::File {
844            return Err(KernelError::FsError(FsError::NotAFile));
845        }
846
847        let old_start = *self.start_cluster.read();
848        let old_size = self.metadata.read().size;
849
850        if size == 0 {
851            // Free all clusters
852            if old_start >= 2 {
853                let mut state = self.fs_state.write();
854                state.fat.free_chain(old_start);
855                state.dirty = true;
856            }
857            *self.start_cluster.write() = 0;
858        } else if size != old_size {
859            // Re-read, resize, re-write
860            let mut data = {
861                let state = self.fs_state.read();
862                state.read_chain_data(old_start, old_size)
863            };
864
865            data.resize(size, 0);
866            let new_cluster = {
867                let mut state = self.fs_state.write();
868                state.write_data(old_start, &data)?
869            };
870            *self.start_cluster.write() = new_cluster;
871        }
872
873        let mut meta = self.metadata.write();
874        meta.size = size;
875        meta.modified = crate::arch::timer::get_timestamp_secs();
876
877        Ok(())
878    }
879}
880
881/// FAT32 filesystem
882pub struct Fat32Fs {
883    root: Arc<Fat32Node>,
884    state: Arc<RwLock<Fat32State>>,
885    readonly: bool,
886}
887
888impl Fat32Fs {
889    /// Create a FAT32 filesystem from raw image data.
890    ///
891    /// `image_data` should be a complete FAT32 volume image.
892    /// The image is loaded entirely into memory (sector cache).
893    pub fn from_image(image_data: &[u8], readonly: bool) -> Result<Self, KernelError> {
894        if image_data.len() < 512 {
895            return Err(KernelError::FsError(FsError::CorruptedData));
896        }
897
898        let bpb = Bpb::parse(&image_data[..512])?;
899        let bps = bpb.bytes_per_sector as usize;
900
901        // Load FAT into memory
902        let fat_start = bpb.reserved_sectors as usize * bps;
903        let fat_size_bytes = bpb.fat_size_32 as usize * bps;
904        let total_data_sectors = bpb.total_sectors_32 as usize - bpb.data_start_sector() as usize;
905        let total_clusters = total_data_sectors / bpb.sectors_per_cluster as usize + 2;
906
907        let mut fat = FatTable::new(total_clusters);
908        if fat_start + fat_size_bytes <= image_data.len() {
909            let fat_data = &image_data[fat_start..fat_start + fat_size_bytes];
910            for i in 0..core::cmp::min(total_clusters, fat_data.len() / 4) {
911                fat.entries[i] = u32::from_le_bytes([
912                    fat_data[i * 4],
913                    fat_data[i * 4 + 1],
914                    fat_data[i * 4 + 2],
915                    fat_data[i * 4 + 3],
916                ]);
917            }
918        }
919
920        // Load all sectors into cache
921        let mut sector_cache = BTreeMap::new();
922        let total_sectors = image_data.len() / bps;
923        for s in 0..total_sectors {
924            let offset = s * bps;
925            let end = core::cmp::min(offset + bps, image_data.len());
926            sector_cache.insert(s as u64, image_data[offset..end].to_vec());
927        }
928
929        let state = Arc::new(RwLock::new(Fat32State {
930            bpb: bpb.clone(),
931            fat,
932            sector_cache,
933            dirty: false,
934            next_free_hint: 2,
935        }));
936
937        let root_inode = FAT32_NEXT_INODE.fetch_add(1, Ordering::Relaxed);
938        let root = Arc::new(Fat32Node::new_directory(
939            root_inode,
940            root_inode,
941            bpb.root_cluster,
942            state.clone(),
943        ));
944
945        Ok(Self {
946            root,
947            state,
948            readonly,
949        })
950    }
951}
952
953impl Filesystem for Fat32Fs {
954    fn root(&self) -> Arc<dyn VfsNode> {
955        self.root.clone() as Arc<dyn VfsNode>
956    }
957
958    fn name(&self) -> &str {
959        "fat32"
960    }
961
962    fn is_readonly(&self) -> bool {
963        self.readonly
964    }
965
966    fn sync(&self) -> Result<(), KernelError> {
967        // In a full implementation, this would flush the sector cache and FAT
968        // back to the block device. For now, all data is in-memory.
969        Ok(())
970    }
971}
972
973#[cfg(test)]
974mod tests {
975    use super::*;
976
977    /// Create a minimal FAT32 image for testing
978    fn make_test_fat32_image() -> Vec<u8> {
979        let bps: usize = 512;
980        let spc: usize = 1;
981        let reserved: usize = 32;
982        let num_fats: usize = 2;
983        let fat_size: usize = 128; // sectors per FAT
984        let total_sectors: usize = reserved + num_fats * fat_size + 1024; // ~512KB data
985
986        let mut image = vec![0u8; total_sectors * bps];
987
988        // Write BPB
989        image[11] = (bps & 0xFF) as u8;
990        image[12] = ((bps >> 8) & 0xFF) as u8;
991        image[13] = spc as u8;
992        image[14] = (reserved & 0xFF) as u8;
993        image[15] = ((reserved >> 8) & 0xFF) as u8;
994        image[16] = num_fats as u8;
995        let ts = total_sectors as u32;
996        image[32] = (ts & 0xFF) as u8;
997        image[33] = ((ts >> 8) & 0xFF) as u8;
998        image[34] = ((ts >> 16) & 0xFF) as u8;
999        image[35] = ((ts >> 24) & 0xFF) as u8;
1000        let fs = fat_size as u32;
1001        image[36] = (fs & 0xFF) as u8;
1002        image[37] = ((fs >> 8) & 0xFF) as u8;
1003        image[38] = ((fs >> 16) & 0xFF) as u8;
1004        image[39] = ((fs >> 24) & 0xFF) as u8;
1005        // Root cluster = 2
1006        image[44] = 2;
1007        // FSInfo sector = 1
1008        image[48] = 1;
1009        // Boot signature
1010        image[510] = 0x55;
1011        image[511] = 0xAA;
1012
1013        // Write FAT: cluster 0 and 1 are reserved
1014        let fat_start = reserved * bps;
1015        // Cluster 0: media descriptor
1016        image[fat_start] = 0xF8;
1017        image[fat_start + 1] = 0xFF;
1018        image[fat_start + 2] = 0xFF;
1019        image[fat_start + 3] = 0x0F;
1020        // Cluster 1: EOC
1021        image[fat_start + 4] = 0xFF;
1022        image[fat_start + 5] = 0xFF;
1023        image[fat_start + 6] = 0xFF;
1024        image[fat_start + 7] = 0x0F;
1025        // Cluster 2 (root dir): EOC
1026        image[fat_start + 8] = 0xFF;
1027        image[fat_start + 9] = 0xFF;
1028        image[fat_start + 10] = 0xFF;
1029        image[fat_start + 11] = 0x0F;
1030
1031        // Data start = (reserved + num_fats * fat_size) * bps
1032        // Cluster 2 = root directory -- leave empty for now
1033
1034        image
1035    }
1036
1037    #[test]
1038    fn test_bpb_parse() {
1039        let image = make_test_fat32_image();
1040        let bpb = Bpb::parse(&image[..512]).unwrap();
1041        assert_eq!(bpb.bytes_per_sector, 512);
1042        assert_eq!(bpb.sectors_per_cluster, 1);
1043        assert_eq!(bpb.reserved_sectors, 32);
1044        assert_eq!(bpb.num_fats, 2);
1045        assert_eq!(bpb.root_cluster, 2);
1046    }
1047
1048    #[test]
1049    fn test_bpb_parse_bad_signature() {
1050        let mut image = vec![0u8; 512];
1051        let result = Bpb::parse(&image);
1052        assert!(result.is_err());
1053    }
1054
1055    #[test]
1056    fn test_fat32_from_image() {
1057        let image = make_test_fat32_image();
1058        let fs = Fat32Fs::from_image(&image, false).unwrap();
1059        assert_eq!(fs.name(), "fat32");
1060        assert!(!fs.is_readonly());
1061
1062        let root = fs.root();
1063        assert_eq!(root.node_type(), NodeType::Directory);
1064    }
1065
1066    #[test]
1067    fn test_fat32_readonly() {
1068        let image = make_test_fat32_image();
1069        let fs = Fat32Fs::from_image(&image, true).unwrap();
1070        assert!(fs.is_readonly());
1071    }
1072
1073    #[test]
1074    fn test_fat32_empty_root_readdir() {
1075        let image = make_test_fat32_image();
1076        let fs = Fat32Fs::from_image(&image, false).unwrap();
1077        let root = fs.root();
1078
1079        let entries = root.readdir().unwrap();
1080        // Just . and ..
1081        assert_eq!(entries.len(), 2);
1082    }
1083
1084    #[test]
1085    fn test_fat32_create_and_write_file() {
1086        let image = make_test_fat32_image();
1087        let fs = Fat32Fs::from_image(&image, false).unwrap();
1088        let root = fs.root();
1089
1090        let file = root.create("TEST.TXT", Permissions::default()).unwrap();
1091        file.write(0, b"Hello FAT32!").unwrap();
1092
1093        let mut buf = vec![0u8; 20];
1094        let n = file.read(0, &mut buf).unwrap();
1095        assert_eq!(n, 12);
1096        assert_eq!(&buf[..12], b"Hello FAT32!");
1097
1098        let meta = file.metadata().unwrap();
1099        assert_eq!(meta.size, 12);
1100    }
1101
1102    #[test]
1103    fn test_fat32_mkdir() {
1104        let image = make_test_fat32_image();
1105        let fs = Fat32Fs::from_image(&image, false).unwrap();
1106        let root = fs.root();
1107
1108        let dir = root.mkdir("SUBDIR", Permissions::default()).unwrap();
1109        assert_eq!(dir.node_type(), NodeType::Directory);
1110
1111        let found = root.lookup("SUBDIR").unwrap();
1112        assert_eq!(found.node_type(), NodeType::Directory);
1113    }
1114
1115    #[test]
1116    fn test_fat32_unlink_file() {
1117        let image = make_test_fat32_image();
1118        let fs = Fat32Fs::from_image(&image, false).unwrap();
1119        let root = fs.root();
1120
1121        root.create("DEL.TXT", Permissions::default()).unwrap();
1122        assert!(root.lookup("DEL.TXT").is_ok());
1123
1124        root.unlink("DEL.TXT").unwrap();
1125        assert!(root.lookup("DEL.TXT").is_err());
1126    }
1127
1128    #[test]
1129    fn test_fat32_truncate() {
1130        let image = make_test_fat32_image();
1131        let fs = Fat32Fs::from_image(&image, false).unwrap();
1132        let root = fs.root();
1133
1134        let file = root.create("TRUNC.TXT", Permissions::default()).unwrap();
1135        file.write(0, b"0123456789").unwrap();
1136        assert_eq!(file.metadata().unwrap().size, 10);
1137
1138        file.truncate(5).unwrap();
1139        assert_eq!(file.metadata().unwrap().size, 5);
1140
1141        let mut buf = vec![0u8; 10];
1142        let n = file.read(0, &mut buf).unwrap();
1143        assert_eq!(n, 5);
1144        assert_eq!(&buf[..5], b"01234");
1145    }
1146
1147    #[test]
1148    fn test_fat_table_alloc_and_free() {
1149        let mut fat = FatTable::new(100);
1150
1151        // All clusters start free
1152        let c1 = fat.alloc_cluster(2).unwrap();
1153        assert_eq!(c1, 2);
1154        assert_eq!(fat.get(2), FAT32_EOC & 0x0FFF_FFFF);
1155
1156        let c2 = fat.alloc_cluster(2).unwrap();
1157        assert_eq!(c2, 3);
1158
1159        // Free cluster 2
1160        fat.free_chain(2);
1161        assert_eq!(fat.get(2), FAT32_FREE);
1162    }
1163
1164    #[test]
1165    fn test_fat_table_chain() {
1166        let mut fat = FatTable::new(100);
1167
1168        fat.set(2, 3);
1169        fat.set(3, 4);
1170        fat.set(4, FAT32_EOC);
1171
1172        let chain = fat.get_chain(2);
1173        assert_eq!(chain, vec![2, 3, 4]);
1174    }
1175
1176    #[test]
1177    fn test_short_name_parsing() {
1178        let mut entry = [0u8; 32];
1179        entry[..8].copy_from_slice(b"TEST    ");
1180        entry[8..11].copy_from_slice(b"TXT");
1181        entry[11] = 0x20; // Archive attribute
1182                          // SAFETY: entry is a stack-local 32-byte array matching RawDirEntry layout.
1183        let raw = unsafe { &*(entry.as_ptr() as *const RawDirEntry) };
1184        assert_eq!(raw.short_name(), "TEST.TXT");
1185    }
1186
1187    #[test]
1188    fn test_short_name_no_extension() {
1189        let mut entry = [0u8; 32];
1190        entry[..8].copy_from_slice(b"README  ");
1191        entry[8..11].copy_from_slice(b"   ");
1192        entry[11] = 0x10; // Directory
1193                          // SAFETY: entry is a stack-local 32-byte array matching RawDirEntry layout.
1194        let raw = unsafe { &*(entry.as_ptr() as *const RawDirEntry) };
1195        assert_eq!(raw.short_name(), "README");
1196    }
1197}