Browse Source

feat(fat32): support of long file names

greatbridf 3 months ago
parent
commit
d5421e4f17
3 changed files with 308 additions and 174 deletions
  1. 32 174
      src/fs/fat32.rs
  2. 239 0
      src/fs/fat32/dir.rs
  3. 37 0
      src/fs/fat32/file.rs

+ 32 - 174
src/fs/fat32.rs

@@ -5,16 +5,17 @@ use alloc::{
     sync::{Arc, Weak},
     vec::Vec,
 };
-use bindings::{EINVAL, EIO};
+use bindings::EIO;
 
-use itertools::Itertools;
+use dir::Dirs as _;
+use file::ClusterRead;
 
 use crate::{
     io::{Buffer, RawBuffer, UninitBuffer},
     kernel::{
         block::{make_device, BlockDevice, BlockDeviceRequest},
         constants::{S_IFDIR, S_IFREG},
-        mem::{paging::Page, phys::PhysPtr},
+        mem::paging::Page,
         vfs::{
             dentry::Dentry,
             inode::{define_struct_inode, Ino, Inode, InodeData},
@@ -27,82 +28,10 @@ use crate::{
     KResult,
 };
 
-type ClusterNo = u32;
-
-const ATTR_RO: u8 = 0x01;
-const ATTR_HIDDEN: u8 = 0x02;
-const ATTR_SYSTEM: u8 = 0x04;
-const ATTR_VOLUME_ID: u8 = 0x08;
-const ATTR_DIRECTORY: u8 = 0x10;
-const ATTR_ARCHIVE: u8 = 0x20;
-
-const RESERVED_FILENAME_LOWERCASE: u8 = 0x08;
-
-#[repr(C, packed)]
-struct FatDirectoryEntry {
-    name: [u8; 8],
-    extension: [u8; 3],
-    attr: u8,
-    reserved: u8,
-    create_time_tenth: u8,
-    create_time: u16,
-    create_date: u16,
-    access_date: u16,
-    cluster_high: u16,
-    modify_time: u16,
-    modify_date: u16,
-    cluster_low: u16,
-    size: u32,
-}
-
-impl FatDirectoryEntry {
-    pub fn filename(&self) -> Arc<[u8]> {
-        let fnpos = self.name.iter().position(|&c| c == ' ' as u8).unwrap_or(8);
-        let mut name = self.name[..fnpos].to_vec();
-
-        let extpos = self
-            .extension
-            .iter()
-            .position(|&c| c == ' ' as u8)
-            .unwrap_or(3);
-
-        if extpos != 0 {
-            name.push('.' as u8);
-            name.extend_from_slice(&self.extension[..extpos]);
-        }
-
-        if self.reserved & RESERVED_FILENAME_LOWERCASE != 0 {
-            name.make_ascii_lowercase();
-        }
-
-        name.into()
-    }
-
-    pub fn ino(&self) -> Ino {
-        let cluster_high = (self.cluster_high as u32) << 16;
-        (self.cluster_low as u32 | cluster_high) as Ino
-    }
-
-    fn is_volume_id(&self) -> bool {
-        self.attr & ATTR_VOLUME_ID != 0
-    }
-
-    fn is_free(&self) -> bool {
-        self.name[0] == 0x00
-    }
-
-    fn is_deleted(&self) -> bool {
-        self.name[0] == 0xE5
-    }
-
-    fn is_invalid(&self) -> bool {
-        self.is_volume_id() || self.is_free() || self.is_deleted()
-    }
+mod dir;
+mod file;
 
-    fn is_directory(&self) -> bool {
-        self.attr & ATTR_DIRECTORY != 0
-    }
-}
+type ClusterNo = u32;
 
 #[derive(Clone, Copy)]
 #[repr(C, packed)]
@@ -265,63 +194,6 @@ impl<'fat> ClusterIterator<'fat> {
     fn new(fat: &'fat [ClusterNo], start: ClusterNo) -> Self {
         Self { fat, cur: start }
     }
-
-    fn read<'closure, 'vfs>(
-        self,
-        vfs: &'vfs FatFs,
-        offset: usize,
-    ) -> impl Iterator<Item = KResult<&'closure [u8]>> + 'closure
-    where
-        'fat: 'closure,
-        'vfs: 'closure,
-    {
-        let cluster_size = vfs.sectors_per_cluster as usize * 512;
-
-        let skip_count = offset / cluster_size;
-        let mut inner_offset = offset % cluster_size;
-
-        let page_buffer = Page::alloc_one();
-
-        self.skip(skip_count).map(move |cluster| {
-            vfs.read_cluster(cluster, &page_buffer)?;
-
-            let data = page_buffer
-                .as_cached()
-                .as_slice::<u8>(page_buffer.len())
-                .split_at(inner_offset)
-                .1;
-            inner_offset = 0;
-
-            Ok(data)
-        })
-    }
-
-    fn dirs<'closure, 'vfs>(
-        self,
-        vfs: &'vfs FatFs,
-        offset: usize,
-    ) -> impl Iterator<Item = KResult<&'closure FatDirectoryEntry>> + 'closure
-    where
-        'fat: 'closure,
-        'vfs: 'closure,
-    {
-        const ENTRY_SIZE: usize = core::mem::size_of::<FatDirectoryEntry>();
-        self.read(vfs, offset)
-            .map(|result| {
-                let data = result?;
-                if data.len() % ENTRY_SIZE != 0 {
-                    return Err(EINVAL);
-                }
-
-                Ok(unsafe {
-                    core::slice::from_raw_parts(
-                        data.as_ptr() as *const FatDirectoryEntry,
-                        data.len() / ENTRY_SIZE,
-                    )
-                })
-            })
-            .flatten_ok()
-    }
 }
 
 impl<'fat> Iterator for ClusterIterator<'fat> {
@@ -380,6 +252,10 @@ impl Inode for FileInode {
         let vfs = vfs.as_any().downcast_ref::<FatFs>().unwrap();
         let fat = vfs.fat.lock_shared();
 
+        if self.size.load(Ordering::Relaxed) as usize == 0 {
+            return Ok(0);
+        }
+
         let iter = ClusterIterator::new(fat.as_ref(), self.ino as ClusterNo).read(vfs, offset);
 
         for data in iter {
@@ -417,34 +293,25 @@ impl Inode for DirInode {
         let vfs = vfs.as_any().downcast_ref::<FatFs>().unwrap();
         let fat = vfs.fat.lock_shared();
 
-        let mut entries = ClusterIterator::new(fat.as_ref(), self.ino as ClusterNo).dirs(vfs, 0);
-
-        let entry = entries.find_map(|entry| {
-            if entry.is_err() {
-                return Some(entry);
-            }
-
-            let entry = entry.unwrap();
+        let mut entries = ClusterIterator::new(fat.as_ref(), self.ino as ClusterNo)
+            .read(vfs, 0)
+            .dirs();
 
-            if !entry.is_invalid() && entry.filename().eq(dentry.name()) {
-                Some(Ok(entry))
-            } else {
-                None
-            }
+        let entry = entries.find(|entry| {
+            entry
+                .as_ref()
+                .map(|entry| &entry.filename == dentry.name())
+                .unwrap_or(true)
         });
 
         match entry {
             None => Ok(None),
             Some(Err(err)) => Err(err),
-            Some(Ok(entry)) => {
-                let ino = entry.ino();
-
-                Ok(Some(vfs.get_or_alloc_inode(
-                    ino,
-                    entry.is_directory(),
-                    entry.size,
-                )))
-            }
+            Some(Ok(entry)) => Ok(Some(vfs.get_or_alloc_inode(
+                entry.cluster as Ino,
+                entry.is_directory,
+                entry.size,
+            ))),
         }
     }
 
@@ -457,32 +324,23 @@ impl Inode for DirInode {
         let vfs = vfs.as_any().downcast_ref::<FatFs>().unwrap();
         let fat = vfs.fat.lock_shared();
 
-        const ENTRY_SIZE: usize = core::mem::size_of::<FatDirectoryEntry>();
-        let cluster_iter =
-            ClusterIterator::new(fat.as_ref(), self.ino as ClusterNo).dirs(vfs, offset);
+        let cluster_iter = ClusterIterator::new(fat.as_ref(), self.ino as ClusterNo)
+            .read(vfs, offset)
+            .dirs();
 
-        let mut nread = 0;
+        let mut nread = 0usize;
         for entry in cluster_iter {
             let entry = entry?;
 
-            if entry.is_invalid() {
-                nread += 1;
-                continue;
-            }
-
-            let ino = entry.ino();
-            let name = entry.filename();
-
-            vfs.get_or_alloc_inode(ino, entry.is_directory(), entry.size);
-
-            if callback(name.as_ref(), ino)?.is_break() {
+            vfs.get_or_alloc_inode(entry.cluster as Ino, entry.is_directory, entry.size);
+            if callback(&entry.filename, entry.cluster as Ino)?.is_break() {
                 break;
             }
 
-            nread += 1;
+            nread += entry.entry_offset as usize;
         }
 
-        Ok(nread * ENTRY_SIZE)
+        Ok(nread)
     }
 }
 

+ 239 - 0
src/fs/fat32/dir.rs

@@ -0,0 +1,239 @@
+use crate::prelude::*;
+
+use alloc::{string::String, sync::Arc};
+use itertools::Itertools;
+
+use super::{bindings::EINVAL, file::ClusterReadIterator};
+
+#[repr(C, packed)]
+pub(super) struct RawDirEntry {
+    name: [u8; 8],
+    extension: [u8; 3],
+    attr: u8,
+    reserved: u8,
+    create_time_tenth: u8,
+    create_time: u16,
+    create_date: u16,
+    access_date: u16,
+    cluster_high: u16,
+    modify_time: u16,
+    modify_date: u16,
+    cluster_low: u16,
+    size: u32,
+}
+
+pub(super) struct FatDirectoryEntry {
+    pub filename: Arc<[u8]>,
+    pub cluster: u32,
+    pub size: u32,
+    pub entry_offset: u32,
+    pub is_directory: bool,
+    // TODO:
+    // create_time: u32,
+    // modify_time: u32,
+}
+
+impl RawDirEntry {
+    const ATTR_RO: u8 = 0x01;
+    const ATTR_HIDDEN: u8 = 0x02;
+    const ATTR_SYSTEM: u8 = 0x04;
+    const ATTR_VOLUME_ID: u8 = 0x08;
+    const ATTR_DIRECTORY: u8 = 0x10;
+    const ATTR_ARCHIVE: u8 = 0x20;
+
+    const RESERVED_FILENAME_LOWERCASE: u8 = 0x08;
+
+    fn filename(&self) -> &[u8] {
+        self.name.trim_ascii_end()
+    }
+
+    fn extension(&self) -> &[u8] {
+        self.extension.trim_ascii_end()
+    }
+
+    fn is_filename_lowercase(&self) -> bool {
+        self.reserved & Self::RESERVED_FILENAME_LOWERCASE != 0
+    }
+
+    fn is_long_filename(&self) -> bool {
+        self.attr == (Self::ATTR_RO | Self::ATTR_HIDDEN | Self::ATTR_SYSTEM | Self::ATTR_VOLUME_ID)
+    }
+
+    fn is_volume_id(&self) -> bool {
+        self.attr & Self::ATTR_VOLUME_ID != 0
+    }
+
+    fn is_free(&self) -> bool {
+        self.name[0] == 0x00
+    }
+
+    fn is_deleted(&self) -> bool {
+        self.name[0] == 0xE5
+    }
+
+    fn is_invalid(&self) -> bool {
+        self.is_volume_id() || self.is_free() || self.is_deleted()
+    }
+
+    fn is_directory(&self) -> bool {
+        self.attr & Self::ATTR_DIRECTORY != 0
+    }
+
+    fn long_filename(&self) -> Option<[u16; 13]> {
+        if !self.is_long_filename() {
+            return None;
+        }
+
+        let mut name = [0; 13];
+        name[0] = u16::from_le_bytes([self.name[1], self.name[2]]);
+        name[1] = u16::from_le_bytes([self.name[3], self.name[4]]);
+        name[2] = u16::from_le_bytes([self.name[5], self.name[6]]);
+        name[3] = u16::from_le_bytes([self.name[7], self.extension[0]]);
+        name[4] = u16::from_le_bytes([self.extension[1], self.extension[2]]);
+        name[5] = self.create_time;
+        name[6] = self.create_date;
+        name[7] = self.access_date;
+        name[8] = self.cluster_high;
+        name[9] = self.modify_time;
+        name[10] = self.modify_date;
+        name[11] = self.size as u16;
+        name[12] = (self.size >> 16) as u16;
+
+        Some(name)
+    }
+}
+
+impl<'data, I> RawDirs<'data> for I where I: ClusterReadIterator<'data> {}
+trait RawDirs<'data>: ClusterReadIterator<'data> {
+    fn raw_dirs(self) -> impl Iterator<Item = KResult<&'data RawDirEntry>> + 'data
+    where
+        Self: Sized,
+    {
+        const ENTRY_SIZE: usize = size_of::<RawDirEntry>();
+
+        self.map(|result| {
+            let data = result?;
+            if data.len() % ENTRY_SIZE != 0 {
+                return Err(EINVAL);
+            }
+
+            Ok(unsafe {
+                core::slice::from_raw_parts(
+                    data.as_ptr() as *const RawDirEntry,
+                    data.len() / ENTRY_SIZE,
+                )
+            })
+        })
+        .flatten_ok()
+    }
+}
+
+pub(super) trait Dirs<'data>: ClusterReadIterator<'data> {
+    fn dirs(self) -> impl Iterator<Item = KResult<FatDirectoryEntry>> + 'data
+    where
+        Self: Sized;
+}
+
+impl<'data, I> Dirs<'data> for I
+where
+    I: ClusterReadIterator<'data>,
+{
+    fn dirs(self) -> impl Iterator<Item = KResult<FatDirectoryEntry>> + 'data
+    where
+        Self: Sized,
+    {
+        self.raw_dirs().real_dirs()
+    }
+}
+
+trait RealDirs<'data>: Iterator<Item = KResult<&'data RawDirEntry>> + 'data {
+    fn real_dirs(self) -> DirsIter<'data, Self>
+    where
+        Self: Sized;
+}
+
+impl<'data, I> RealDirs<'data> for I
+where
+    I: Iterator<Item = KResult<&'data RawDirEntry>> + 'data,
+{
+    fn real_dirs(self) -> DirsIter<'data, Self>
+    where
+        Self: Sized,
+    {
+        DirsIter { iter: self }
+    }
+}
+
+pub(super) struct DirsIter<'data, I>
+where
+    I: Iterator<Item = KResult<&'data RawDirEntry>> + 'data,
+{
+    iter: I,
+}
+
+impl<'data, I> Iterator for DirsIter<'data, I>
+where
+    I: Iterator<Item = KResult<&'data RawDirEntry>> + 'data,
+{
+    type Item = KResult<FatDirectoryEntry>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let mut filename = String::new();
+        let mut entry_offset = 0;
+        let entry = loop {
+            let entry = match self.iter.next()? {
+                Ok(entry) => entry,
+                Err(err) => return Some(Err(err)),
+            };
+            entry_offset += 1;
+
+            let long_filename = entry.long_filename();
+            if entry.is_invalid() {
+                if let Some(long_filename) = long_filename {
+                    let long_filename = long_filename
+                        .iter()
+                        .position(|&ch| ch == 0)
+                        .map(|pos| &long_filename[..pos])
+                        .unwrap_or(&long_filename);
+
+                    filename.extend(
+                        long_filename
+                            .into_iter()
+                            .map(|&ch| char::from_u32(ch as u32).unwrap_or('?'))
+                            .rev(),
+                    );
+                }
+                continue;
+            }
+            break entry;
+        };
+
+        let filename: Arc<[u8]> = if filename.is_empty() {
+            let mut filename = entry.filename().to_vec();
+            let extension = entry.extension();
+            if !extension.is_empty() {
+                filename.push(b'.');
+                filename.extend_from_slice(extension);
+            }
+
+            if entry.is_filename_lowercase() {
+                filename.make_ascii_lowercase();
+            }
+
+            filename.into()
+        } else {
+            let mut bytes = filename.into_bytes();
+            bytes.reverse();
+
+            bytes.into()
+        };
+
+        Some(Ok(FatDirectoryEntry {
+            size: entry.size,
+            entry_offset,
+            filename,
+            cluster: entry.cluster_low as u32 | ((entry.cluster_high as u32) << 16),
+            is_directory: entry.is_directory(),
+        }))
+    }
+}

+ 37 - 0
src/fs/fat32/file.rs

@@ -0,0 +1,37 @@
+use crate::{
+    kernel::mem::{phys::PhysPtr, Page},
+    KResult,
+};
+
+use super::{ClusterIterator, FatFs};
+
+pub trait ClusterReadIterator<'data>: Iterator<Item = KResult<&'data [u8]>> + 'data {}
+impl<'a, I> ClusterReadIterator<'a> for I where I: Iterator<Item = KResult<&'a [u8]>> + 'a {}
+
+pub(super) trait ClusterRead<'data> {
+    fn read<'vfs>(self, vfs: &'vfs FatFs, offset: usize) -> impl ClusterReadIterator<'data>
+    where
+        Self: Sized,
+        'vfs: 'data;
+}
+
+impl<'data, 'fat: 'data> ClusterRead<'data> for ClusterIterator<'fat> {
+    fn read<'vfs: 'data>(self, vfs: &'vfs FatFs, offset: usize) -> impl ClusterReadIterator<'data> {
+        const SECTOR_SIZE: usize = 512;
+
+        let cluster_size = vfs.sectors_per_cluster as usize * SECTOR_SIZE;
+        assert!(cluster_size <= 0x1000, "Cluster size is too large");
+
+        let skip_clusters = offset / cluster_size;
+        let mut inner_offset = offset % cluster_size;
+
+        let buffer_page = Page::alloc_one();
+
+        self.skip(skip_clusters).map(move |cluster| {
+            vfs.read_cluster(cluster, &buffer_page)?;
+            let data = &buffer_page.as_cached().as_slice(buffer_page.len())[inner_offset..];
+            inner_offset = 0;
+            Ok(data)
+        })
+    }
+}