use alloc::{ boxed::Box, string::{String, ToString}, sync::Arc, vec::Vec, }; use crate::drivers::storage::Partition; use super::vfs::{FsOps, VNode, VNodeOperations}; // The first Cluster (perhaps 0xF0FFFF0F) is the FAT ID // The second cluster stores the end-of-cluster-chain marker // The third entry and further holds the directory table // // Fat Clusters are either one of these types: // // | fat12 | fat16 | fat32 | Description | // |---------------|-----------------|-------------------------|-------------------------------| // | 0xFF8-0xFFF | 0xFFF8-0xFFFF | 0x0FFFFFF8-0x0FFFFFFF | End Of cluster Chain | // | 0xFF7 | 0xFFF7 | 0x0FFFFFF7 | Bad Cluster | // | 0x002-0xFEF | 0x0002-0xFFEF | 0x00000002-0x0FFFFFEF | In use Cluster | // | 0x000 | 0x0000 | 0x00000000 | Free Cluster | // End Of Chain const EOC_12: u32 = 0x0FF8; const EOC_16: u32 = 0xFFF8; const EOC_32: u32 = 0x0FFFFFF8; #[derive(Clone, Copy, Debug)] enum FatType { Fat12(Fat16EBPB), Fat16(Fat16EBPB), Fat32(Fat32EBPB), } #[repr(C, packed)] #[derive(Clone, Copy, Debug)] pub struct BIOSParameterBlock { _jmp_instruction: [u8; 3], // EB 58 90 pub oem_identifier: [u8; 8], // MTOO4043 (hey, mtools) pub bytes_per_sector: u16, // 00 02 (little endian so 512) pub sectors_per_cluster: u8, // 01 pub reserved_sectors: u16, // 20 00 (32) pub fat_count: u8, // 02 pub root_directory_count: u16, // 00 00 (what) pub total_sectors: u16, // equal to zero when sector count is more than 65535 pub media_descriptor_type: u8, // F0 pub sectors_per_fat: u16, // Fat12/Fat16 only pub sectors_per_track: u16, // 3F 00 (63) pub head_count: u16, // 10 00 (16) pub hidden_sectors: u32, // 00 00 00 00 pub large_sector_count: u32, // 00 F8 01 00 (129024) pub ebpb_bytes: [u8; 54], } // Fat 12 and Fat 16 EBPB #[repr(C, packed)] #[derive(Clone, Copy, Debug)] pub struct Fat16EBPB { pub drive_number: u8, _reserved: u8, pub signature: u8, pub volume_id: u32, pub volume_label: [u8; 11], pub system_identifier_string: [u8; 8], } #[repr(C, packed)] #[derive(Clone, Copy, Debug)] pub struct Fat32EBPB { pub sectors_per_fat_ext: u32, // E1 03 00 00 (993, wtf) pub flags: [u8; 2], // 00 00 pub fat_version: u16, // 00 00 pub root_dir_cluster: u32, // 2C 00 00 00 (2) pub fsinfo_sector: u16, // 01 00 (1) pub backup_bootsector: u16, // 06 00 (6) _reserved: [u8; 12], // all zero pub drive_number: u8, // 00 _reserved2: u8, // 00 pub signature: u8, // either 0x28 of 0x29: 29 pub volume_id: u32, // Varies pub volume_label: [u8; 11], // "NO NAME " pub system_identifier_string: [u8; 8], // Always "FAT32 " but never trust the contents of this string (for some reason) } #[repr(C, packed)] #[derive(Debug)] pub struct FSInfo { pub lead_signature: u32, pub mid_signature: u32, pub last_known_free_cluster: u32, pub look_for_free_clusters: u32, _reserved2: [u8; 12], pub trail_signature: u32, } impl FSInfo { pub fn from_bytes(bytes: Arc<[u8]>) -> Self { assert!(bytes.len() >= 512); let lead_signature = u32::from_le_bytes(bytes[0..4].try_into().unwrap()); let mid_signature = u32::from_le_bytes(bytes[484..488].try_into().unwrap()); let last_known_free_cluster = u32::from_le_bytes(bytes[488..492].try_into().unwrap()); let look_for_free_clusters = u32::from_le_bytes(bytes[492..496].try_into().unwrap()); let _reserved2 = bytes[496..508].try_into().unwrap(); let trail_signature = u32::from_le_bytes(bytes[508..].try_into().unwrap()); return Self { lead_signature, mid_signature, last_known_free_cluster, look_for_free_clusters, _reserved2, trail_signature, }; } } #[repr(u8)] #[derive(Debug, PartialEq)] #[allow(dead_code)] enum FileEntryAttributes { ReadOnly = 0x01, Hidden = 0x02, System = 0x04, VolumeId = 0x08, Directory = 0x10, Archive = 0x20, // basically any file LongFileName = 0x0F, } #[repr(C, packed)] #[derive(Debug)] struct LongFileName { entry_order: u8, first_characters: [u16; 5], attribute: u8, // always 0x0F long_entry_type: u8, // zero for name entries checksum: u8, second_characters: [u16; 6], _always_zero: [u8; 2], final_characters: [u16; 2], } #[repr(C, packed)] #[derive(Debug)] pub struct FileEntry { file_name: [u8; 8], extension: [u8; 3], attributes: u8, _reserved: u8, creation_tenths: u8, creation_time: u16, creation_date: u16, accessed_date: u16, high_first_cluster_number: u16, // The high 16 bits of this entry's first cluster number. For FAT 12 and FAT 16 this is always zero. modified_time: u16, modified_date: u16, low_first_cluster_number: u16, file_size: u32, } impl FileEntry { pub fn cluster(&self) -> u32 { let mut cluster = self.low_first_cluster_number as u32; cluster |= (self.high_first_cluster_number as u32) << 16; return cluster; } } pub struct FatFs { partition: Partition, // FAT info #[allow(dead_code)] fs_info: Option, fat: Option>, bpb: BIOSParameterBlock, fat_start: u64, fat_type: FatType, cluster_size: usize, sectors_per_fat: usize, } impl FatFs { pub fn new(partition: Partition) -> Result { let bpb_bytes = partition .read(0, 1) .expect("Failed to read FAT32 BIOS Parameter Block!"); let bpb = unsafe { *(bpb_bytes.as_ptr().cast::()) }; let (total_sectors, fat_size) = if bpb.total_sectors == 0 { (bpb.large_sector_count, unsafe { (*bpb.ebpb_bytes.as_ptr().cast::()).sectors_per_fat_ext }) } else { (bpb.total_sectors as u32, bpb.sectors_per_fat as u32) }; let root_dir_sectors = ((bpb.root_directory_count * 32) + (bpb.bytes_per_sector - 1)) / bpb.bytes_per_sector; let total_data_sectors = total_sectors - (bpb.reserved_sectors as u32 + (bpb.fat_count as u32 * fat_size) + root_dir_sectors as u32); let total_clusters = total_data_sectors / bpb.sectors_per_cluster as u32; let fat_type = if total_clusters < 4085 { FatType::Fat12(unsafe { *bpb.ebpb_bytes.as_ptr().cast::() }) } else if total_clusters < 65525 { FatType::Fat16(unsafe { *bpb.ebpb_bytes.as_ptr().cast::() }) } else { FatType::Fat32(unsafe { *bpb.ebpb_bytes.as_ptr().cast::() }) }; let system_ident = match fat_type { FatType::Fat12(ebpb) => ebpb.system_identifier_string, FatType::Fat16(ebpb) => ebpb.system_identifier_string, FatType::Fat32(ebpb) => ebpb.system_identifier_string, }; let system_identifier = core::str::from_utf8(&system_ident); if system_identifier.is_err() { return Err(()); } if let Ok(system_identifier_string) = system_identifier { match fat_type { FatType::Fat12(_) => { if !system_identifier_string.contains("FAT12") { return Err(()); } } FatType::Fat16(_) => { if !system_identifier_string.contains("FAT16") { return Err(()); } } FatType::Fat32(_) => { if !system_identifier_string.contains("FAT32") { return Err(()); } } } } let fs_info = match fat_type { FatType::Fat32(ebpb) => { let fsinfo_bytes = partition .read(ebpb.fsinfo_sector as u64, 1) .expect("Failed to read FSInfo sector!"); Some(FSInfo::from_bytes(fsinfo_bytes)) } _ => None, }; let fat_start = bpb.reserved_sectors as u64; let sectors_per_fat = match fat_type { FatType::Fat32(ebpb) => ebpb.sectors_per_fat_ext as usize, _ => bpb.sectors_per_fat as usize, }; let bytes_per_fat = 512 * sectors_per_fat; let mut fat: Option> = None; if crate::KERNEL_FEATURES.fat_in_mem { let cluster_bytes = match fat_type { FatType::Fat32(_) => 4, _ => 2, }; let mut fat_vec: Vec = Vec::with_capacity(bytes_per_fat / cluster_bytes); for i in 0..sectors_per_fat { let sector = partition .read(fat_start + i as u64, 1) .expect("Failed to read FAT"); for j in 0..(512 / cluster_bytes) { match fat_type { FatType::Fat32(_) => fat_vec.push(u32::from_le_bytes( sector[j * cluster_bytes..(j * cluster_bytes + cluster_bytes)] .try_into() .unwrap(), )), _ => fat_vec.push(u16::from_le_bytes( sector[j * cluster_bytes..(j * cluster_bytes + cluster_bytes)] .try_into() .unwrap(), ) as u32), } } } fat = Some(Arc::from(fat_vec)); } else { crate::log_info!( "\x1B[33mWARNING\x1B[0m: FAT is not being stored in memory, this feature is experimental and file reads are expected to be slower." ) } crate::println!("Found {fat_type:?} FS"); let cluster_size = bpb.sectors_per_cluster as usize * 512; return Ok(Self { partition, fs_info, fat, bpb, fat_start, fat_type, cluster_size, sectors_per_fat, }); } fn find_entry_in_directory(&self, cluster: usize, name: &str) -> Result { let mut i: usize = 0; // Long file name is stored outsize because long filename and the real entry on separate entries let mut long_filename: Vec = Vec::new(); let mut long_filename_string: Option = None; let data_sector = self.read_cluster(cluster)?; loop { let bytes: [u8; core::mem::size_of::()] = data_sector[(i * 32)..((i + 1) * 32)].try_into().unwrap(); let first_byte = bytes[0]; let file_entry: FileEntry; i += 1; // Step 1 if first_byte == 0x00 { break; // End of directory listing } // Step 2 if first_byte == 0xE5 { continue; // Directory is unused, ignore it } else if bytes[11] == FileEntryAttributes::LongFileName as u8 { // Entry is LFN (step 3) // read long filename (step 4) let long_filename_part: LongFileName; unsafe { long_filename_part = core::mem::transmute(bytes); } long_filename.push(long_filename_part); continue; } else { // step 5 unsafe { file_entry = core::mem::transmute(bytes); } // step 6 if !long_filename.is_empty() { // Make fileEntry with LFN (step 7) let mut string: Vec = Vec::with_capacity(long_filename.len() * 13); for i in 0..long_filename.len() { let i = (long_filename.len() - 1) - i; let long_filename = &long_filename[i]; let mut character_bytes = Vec::new(); let characters = long_filename.first_characters; character_bytes.extend_from_slice(&characters); let characters = long_filename.second_characters; character_bytes.extend_from_slice(&characters); let characters = long_filename.final_characters; character_bytes.extend_from_slice(&characters); // remove 0x0000 characters and 0xFFFF characters character_bytes.retain(|&x| x != 0xFFFF && x != 0x0000); for &le_character in character_bytes.iter() { // Convert little-endian u16 to native-endian u16 let native_endian_value = u16::from_le(le_character); string.push(native_endian_value); } } long_filename_string = Some(String::from_utf16(&string).unwrap()); long_filename.clear(); } } if name.replacen('.', "", 1).len() <= 11 { let search_parts: Vec<&str> = name.split('.').collect(); let filename = core::str::from_utf8(&file_entry.file_name).unwrap(); let extension = core::str::from_utf8(&file_entry.extension).unwrap(); if (search_parts.len() == 1 && !filename.contains(&search_parts[0].to_ascii_uppercase())) || (search_parts.len() > 1 && (!filename.contains(&search_parts[0].to_ascii_uppercase()) || !extension.contains(&search_parts[1].to_ascii_uppercase()))) { continue; } return Ok(file_entry); } else { // Long file name if long_filename_string != Some(name.to_string()) { continue; } return Ok(file_entry); } } return Err(()); } pub fn read_cluster(&self, cluster: usize) -> Result, ()> { return self.partition.read( self.cluster_to_sector(cluster) as u64, self.bpb.sectors_per_cluster as usize, ); } fn cluster_to_sector(&self, cluster: usize) -> usize { crate::println!("bytes per sector: {}", unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(self.bpb.bytes_per_sector)) }); let fat_size = self.sectors_per_fat; let root_dir_sectors = ((self.bpb.root_directory_count * 32) + (self.bpb.bytes_per_sector - 1)) / self.bpb.bytes_per_sector; let first_data_sector = self.bpb.reserved_sectors as usize + (self.bpb.fat_count as usize * fat_size) + root_dir_sectors as usize; return ((((cluster.wrapping_sub(2)) as isize) .wrapping_mul(self.bpb.sectors_per_cluster as isize)) as usize) .wrapping_add(first_data_sector); } fn sector_to_cluster(&self, sector: usize) -> usize { let fat_size = self.sectors_per_fat; let root_dir_sectors = ((self.bpb.root_directory_count * 32) + (self.bpb.bytes_per_sector - 1)) / self.bpb.bytes_per_sector; let first_data_sector = self.bpb.reserved_sectors as usize + (self.bpb.fat_count as usize * fat_size) + root_dir_sectors as usize; return (((sector).wrapping_sub(first_data_sector)) .wrapping_div(self.bpb.sectors_per_cluster as usize)) .wrapping_add(2); } fn get_next_cluster(&self, cluster: usize) -> u32 { if crate::KERNEL_FEATURES.fat_in_mem { return match self.fat_type { FatType::Fat12(_) => self.fat.as_ref().unwrap()[cluster] & 0x0FFF, FatType::Fat16(_) => self.fat.as_ref().unwrap()[cluster] & 0xFFFF, FatType::Fat32(_) => self.fat.as_ref().unwrap()[cluster] & 0x0FFFFFFF, }; } else { let fat_entry_size = match self.fat_type { FatType::Fat12(_) => 2, // 12 bits per entry FatType::Fat16(_) => 2, // 16 bits per entry FatType::Fat32(_) => 4, // 28 bits per entry }; let entry_offset = cluster * fat_entry_size; let entry_offset_in_sector = entry_offset % 512; // needs two incase we "straddle a sector" let sector_data = self .partition .read(self.fat_start + entry_offset as u64 / 512, 2) .expect("Failed to read from FAT!"); match self.fat_type { FatType::Fat12(_) => { let cluster_entry_bytes: [u8; 2] = sector_data [entry_offset_in_sector..entry_offset_in_sector + 2] .try_into() .unwrap(); return (u16::from_le_bytes(cluster_entry_bytes) & 0x0FFF) as u32; } FatType::Fat16(_) => { let cluster_entry_bytes: [u8; 2] = sector_data [entry_offset_in_sector..entry_offset_in_sector + 2] .try_into() .unwrap(); return (u16::from_le_bytes(cluster_entry_bytes)) as u32; } FatType::Fat32(_) => { let cluster_entry_bytes: [u8; 4] = sector_data [entry_offset_in_sector..entry_offset_in_sector + 4] .try_into() .unwrap(); return u32::from_le_bytes(cluster_entry_bytes) & 0x0FFFFFFF; } } } } } impl FsOps for FatFs { fn mount(&mut self, _path: &str, data: &mut *mut u8, _vfsp: *const super::vfs::Vfs) { // TODO: load the FAT into memory here *data = core::ptr::addr_of!(*self) as *mut u8; } fn unmount(&mut self, _vfsp: *const super::vfs::Vfs) { // TODO: unload the FAT form memory } fn root(&mut self, vfsp: *const super::vfs::Vfs) -> super::vfs::VNode { let root_cluster = match self.fat_type { FatType::Fat32(ebpb) => ebpb.root_dir_cluster as usize, _ => self.sector_to_cluster( self.bpb.reserved_sectors as usize + (self.bpb.fat_count as usize * self.sectors_per_fat), ), }; let file = File::Dir(FatDirectory { directory_cluster: root_cluster, }); return super::vfs::VNode { flags: 0, ref_count: 0, shared_lock_count: 0, exclusive_lock_count: 0, ops: Box::new(file), node_data: None, parent: vfsp, typ: super::vfs::VNodeType::Directory, data: core::ptr::null_mut(), }; } fn fid(&mut self, _path: &str, _vfsp: *const super::vfs::Vfs) -> Option { todo!("FAT FID"); } fn statfs(&mut self, _vfsp: *const super::vfs::Vfs) -> super::vfs::StatFs { todo!("FAT STATFS"); } fn sync(&mut self, _vfsp: *const super::vfs::Vfs) { todo!("FAT SYNC"); } fn vget( &mut self, _fid: super::vfs::FileId, _vfsp: *const super::vfs::Vfs, ) -> super::vfs::VNode { todo!("FAT VGET"); } } enum File { Archive(FatFile), Dir(FatDirectory), } impl<'a> VNodeOperations for File { fn access(&mut self, _m: u32, _c: super::vfs::UserCred, _vp: *const VNode) { todo!("VNODE OPERATIONS"); } fn bmap(&mut self, _block_number: u32, _bnp: (), _vp: *const VNode) -> super::vfs::VNode { todo!("VNODE OPERATIONS"); } fn bread(&mut self, _block_number: u32, _vp: *const VNode) -> Arc<[u8]> { todo!("VNODE OPERATIONS"); } fn close(&mut self, _f: u32, _c: super::vfs::UserCred, _vp: *const VNode) { todo!("VNODE OPERATIONS"); } fn create( &mut self, _nm: &str, _va: super::vfs::VAttr, _e: u32, _m: u32, _c: super::vfs::UserCred, _vp: *const VNode, ) -> Result { todo!("VNODE OPERATIONS"); } fn fsync(&mut self, _c: super::vfs::UserCred, _vp: *const VNode) { todo!("VNODE OPERATIONS"); } fn getattr(&mut self, _c: super::vfs::UserCred, _vp: *const VNode) -> super::vfs::VAttr { todo!("VNODE OPERATIONS"); } fn inactive(&mut self, _c: super::vfs::UserCred, _vp: *const VNode) { todo!("VNODE OPERATIONS"); } fn ioctl( &mut self, _com: u32, _d: *mut u8, _f: u32, _c: super::vfs::UserCred, _vp: *const VNode, ) { todo!("VNODE OPERATIONS"); } fn link( &mut self, _target_dir: *mut super::vfs::VNode, _target_name: &str, _c: super::vfs::UserCred, _vp: *const VNode, ) { todo!("VNODE OPERATIONS"); } fn lookup( &mut self, nm: &str, _c: super::vfs::UserCred, vp: *const VNode, ) -> Result { let fat_fs = unsafe { (*(*vp).parent).data.cast::() }; match self { File::Dir(directory) => unsafe { let file_entry = (*fat_fs).find_entry_in_directory(directory.directory_cluster, nm)?; let file_typ = if file_entry.attributes == FileEntryAttributes::Directory as u8 { crate::drivers::fs::vfs::VNodeType::Directory } else { crate::drivers::fs::vfs::VNodeType::Regular }; let file = if file_entry.attributes == FileEntryAttributes::Directory as u8 { File::Dir(FatDirectory { directory_cluster: file_entry.cluster() as usize, }) } else { File::Archive(FatFile { file_entry }) }; let vnode = VNode { flags: 0, ref_count: 0, shared_lock_count: 0, exclusive_lock_count: 0, ops: Box::new(file), node_data: None, parent: (*vp).parent, typ: file_typ, data: core::ptr::null_mut(), }; Ok(vnode) }, _ => panic!("tried to lookup on a file"), } } fn mkdir( &mut self, _nm: &str, _va: super::vfs::VAttr, _c: super::vfs::UserCred, _vp: *const VNode, ) -> Result { todo!("VNODE OPERATIONS"); } fn open( &mut self, _f: u32, _c: super::vfs::UserCred, vp: *const VNode, ) -> Result, ()> { match self { File::Archive(archive) => { let fat_fs = unsafe { (*(*vp).parent).data.cast::() }; let mut file: Vec = Vec::with_capacity(archive.file_entry.file_size as usize); let mut file_ptr_index = 0; let mut cluster = ((archive.file_entry.high_first_cluster_number as u32) << 16) | archive.file_entry.low_first_cluster_number as u32; let cluster_size = unsafe { (*fat_fs).cluster_size }; let mut copied_bytes = 0; loop { let cluster_data = unsafe { (*fat_fs).read_cluster(cluster as usize)? }; let remaining = archive.file_entry.file_size as usize - copied_bytes; let to_copy = if remaining > cluster_size { cluster_size } else { remaining }; unsafe { core::ptr::copy_nonoverlapping( cluster_data.as_ptr(), file.as_mut_ptr().add(file_ptr_index), to_copy, ); file.set_len(file.len() + to_copy); } file_ptr_index += cluster_size; copied_bytes += to_copy; cluster = unsafe { (*fat_fs).get_next_cluster(cluster as usize) }; match unsafe { (*fat_fs).fat_type } { FatType::Fat12(_) => { if cluster >= EOC_12 { break; } } FatType::Fat16(_) => { if cluster >= EOC_16 { break; } } FatType::Fat32(_) => { if cluster >= EOC_32 { break; } } } } return Ok(Arc::from(file)); } _ => panic!("Cannot open non archives"), } } fn rdwr( &mut self, _uiop: *const super::vfs::UIO, _direction: super::vfs::IODirection, _f: u32, _c: super::vfs::UserCred, _vp: *const VNode, ) { todo!("VNODE OPERATIONS"); } fn readdir( &mut self, _uiop: *const super::vfs::UIO, _c: super::vfs::UserCred, _vp: *const VNode, ) { todo!("VNODE OPERATIONS"); } fn readlink( &mut self, _uiop: *const super::vfs::UIO, _c: super::vfs::UserCred, _vp: *const VNode, ) { todo!("VNODE OPERATIONS"); } fn rename( &mut self, _nm: &str, _target_dir: *mut super::vfs::VNode, _target_name: &str, _c: super::vfs::UserCred, _vp: *const VNode, ) { todo!("VNODE OPERATIONS"); } fn select(&mut self, _w: super::vfs::IODirection, _c: super::vfs::UserCred, _vp: *const VNode) { todo!("VNODE OPERATIONS"); } fn setattr(&mut self, _va: super::vfs::VAttr, _c: super::vfs::UserCred, _vp: *const VNode) { todo!("VNODE OPERATIONS"); } fn strategy(&mut self, _bp: (), _vp: *const VNode) { todo!("VNODE OPERATIONS"); } fn symlink( &mut self, _link_name: &str, _va: super::vfs::VAttr, _target_name: &str, _c: super::vfs::UserCred, _vp: *const VNode, ) { todo!("VNODE OPERATIONS"); } } struct FatFile { file_entry: FileEntry, } struct FatDirectory { directory_cluster: usize, }