Skip to main content

ax_fs/fs/
fatfs.rs

1// Copyright 2025 The Axvisor Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! FAT filesystem implementation
16
17use alloc::{boxed::Box, sync::Arc};
18use core::cell::OnceCell;
19
20use ax_fs_vfs::{
21    VfsDirEntry, VfsError, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, VfsOps,
22    VfsResult,
23};
24use axfatfs::{Dir, File, LossyOemCpConverter, NullTimeProvider, Read, Seek, SeekFrom, Write};
25use spin::Mutex;
26
27use crate::dev::{Disk, Partition};
28
29const BLOCK_SIZE: usize = 512;
30
31/// FAT filesystem implementation
32pub struct FatFileSystem {
33    inner: axfatfs::FileSystem<PartitionWrapper, NullTimeProvider, LossyOemCpConverter>,
34    root_dir: OnceCell<VfsNodeRef>,
35}
36
37/// A wrapper for Partition to implement the required traits for axfatfs
38pub struct PartitionWrapper {
39    partition: Partition,
40}
41
42impl PartitionWrapper {
43    /// Creates a new partition wrapper
44    pub fn new(partition: Partition) -> Self {
45        Self { partition }
46    }
47}
48
49impl axfatfs::IoBase for PartitionWrapper {
50    type Error = ();
51}
52
53impl axfatfs::Read for PartitionWrapper {
54    fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, Self::Error> {
55        let mut read_len = 0;
56        while !buf.is_empty() {
57            match self.partition.read_one(buf) {
58                Ok(0) => break,
59                Ok(n) => {
60                    let tmp = buf;
61                    buf = &mut tmp[n..];
62                    read_len += n;
63                }
64                Err(_) => return Err(()),
65            }
66        }
67        Ok(read_len)
68    }
69}
70
71impl axfatfs::Write for PartitionWrapper {
72    fn write(&mut self, mut buf: &[u8]) -> Result<usize, Self::Error> {
73        let mut write_len = 0;
74        while !buf.is_empty() {
75            match self.partition.write_one(buf) {
76                Ok(0) => break,
77                Ok(n) => {
78                    buf = &buf[n..];
79                    write_len += n;
80                }
81                Err(_) => return Err(()),
82            }
83        }
84        Ok(write_len)
85    }
86
87    fn flush(&mut self) -> Result<(), Self::Error> {
88        Ok(())
89    }
90}
91
92impl axfatfs::Seek for PartitionWrapper {
93    fn seek(&mut self, pos: axfatfs::SeekFrom) -> Result<u64, Self::Error> {
94        let size = self.partition.size();
95        let new_pos = match pos {
96            axfatfs::SeekFrom::Start(pos) => Some(pos),
97            axfatfs::SeekFrom::Current(off) => self.partition.position().checked_add_signed(off),
98            axfatfs::SeekFrom::End(off) => size.checked_add_signed(off),
99        }
100        .ok_or(())?;
101        if new_pos > size {
102            warn!("Seek beyond the end of the partition");
103        }
104        self.partition.set_position(new_pos);
105        Ok(new_pos)
106    }
107}
108
109/// Wrapper for FAT file
110pub struct FileWrapper<'a>(
111    Mutex<File<'a, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>>,
112);
113/// Wrapper for FAT directory
114pub struct DirWrapper<'a>(Dir<'a, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>);
115
116unsafe impl Sync for FatFileSystem {}
117unsafe impl Send for FatFileSystem {}
118unsafe impl Send for FileWrapper<'_> {}
119unsafe impl Sync for FileWrapper<'_> {}
120unsafe impl Send for DirWrapper<'_> {}
121unsafe impl Sync for DirWrapper<'_> {}
122
123impl FatFileSystem {
124    /// Creates a new FAT filesystem from a disk
125    #[allow(dead_code)]
126    pub fn new(disk: Disk) -> Self {
127        let disk_size = disk.size();
128        let wrapper = PartitionWrapper::new(crate::dev::Partition::new(disk, 0, disk_size / 512));
129        let inner = axfatfs::FileSystem::new(wrapper, axfatfs::FsOptions::new())
130            .expect("failed to initialize FAT filesystem");
131        Self {
132            inner,
133            root_dir: OnceCell::new(),
134        }
135    }
136
137    /// Create a new FAT filesystem from a partition
138    pub fn from_partition(partition: Partition) -> Self {
139        let wrapper = PartitionWrapper::new(partition);
140        let inner = axfatfs::FileSystem::new(wrapper, axfatfs::FsOptions::new())
141            .expect("failed to initialize FAT filesystem on partition");
142        Self {
143            inner,
144            root_dir: OnceCell::new(),
145        }
146    }
147
148    /// Initializes the FAT filesystem
149    #[allow(dead_code)]
150    pub fn init(&'static self) {
151        // root_dir is already initialized in new(), so nothing to do here
152    }
153
154    fn new_file(
155        file: File<'_, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>,
156    ) -> VfsNodeRef {
157        // Use a Box to extend the lifetime of the file
158        let file_box = Box::new(file);
159        let file_static = unsafe {
160            core::mem::transmute::<
161                Box<File<'_, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>>,
162                Box<File<'static, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>>,
163            >(file_box)
164        };
165        let file_wrapper = FileWrapper(Mutex::new(*file_static));
166        Arc::new(file_wrapper) as VfsNodeRef
167    }
168
169    fn new_dir(
170        dir: Dir<'_, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>,
171    ) -> VfsNodeRef {
172        // Use a Box to extend the lifetime of the dir
173        let dir_box = Box::new(dir);
174        let dir_static = unsafe {
175            core::mem::transmute::<
176                Box<Dir<'_, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>>,
177                Box<Dir<'static, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>>,
178            >(dir_box)
179        };
180        let dir_wrapper = DirWrapper(*dir_static);
181        Arc::new(dir_wrapper) as VfsNodeRef
182    }
183}
184
185impl VfsNodeOps for FileWrapper<'static> {
186    ax_fs_vfs::impl_vfs_non_dir_default! {}
187
188    fn get_attr(&self) -> VfsResult<VfsNodeAttr> {
189        let size = self.0.lock().seek(SeekFrom::End(0)).map_err(as_vfs_err)?;
190        let blocks = size.div_ceil(BLOCK_SIZE as u64);
191        // FAT fs doesn't support permissions, we just set everything to 755
192        let perm = VfsNodePerm::from_bits_truncate(0o755);
193        Ok(VfsNodeAttr::new(perm, VfsNodeType::File, size, blocks))
194    }
195
196    fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult<usize> {
197        let mut file = self.0.lock();
198        file.seek(SeekFrom::Start(offset)).map_err(as_vfs_err)?; // TODO: more efficient
199        file.read(buf).map_err(as_vfs_err)
200    }
201
202    fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult<usize> {
203        let mut file = self.0.lock();
204        file.seek(SeekFrom::Start(offset)).map_err(as_vfs_err)?; // TODO: more efficient
205        file.write(buf).map_err(as_vfs_err)
206    }
207
208    fn truncate(&self, size: u64) -> VfsResult {
209        let mut file = self.0.lock();
210        let current_size = file.seek(SeekFrom::End(0)).map_err(as_vfs_err)?;
211
212        if size <= current_size {
213            // If the target size is smaller than the current size,
214            // perform a standard truncation operation
215            file.seek(SeekFrom::Start(size)).map_err(as_vfs_err)?; // TODO: more efficient
216            file.truncate().map_err(as_vfs_err)
217        } else {
218            // Calculate the number of bytes to fill
219            let mut zeros_needed = size - current_size;
220            // Create a buffer of zeros
221            let zeros = [0u8; 4096];
222            while zeros_needed > 0 {
223                let to_write = core::cmp::min(zeros_needed, zeros.len() as u64);
224                let write_buf = &zeros[..to_write as usize];
225                file.write(write_buf).map_err(as_vfs_err)?;
226                zeros_needed -= to_write;
227            }
228            Ok(())
229        }
230    }
231}
232
233impl VfsNodeOps for DirWrapper<'static> {
234    ax_fs_vfs::impl_vfs_dir_default! {}
235
236    fn get_attr(&self) -> VfsResult<VfsNodeAttr> {
237        // FAT fs doesn't support permissions, we just set everything to 755
238        Ok(VfsNodeAttr::new(
239            VfsNodePerm::from_bits_truncate(0o755),
240            VfsNodeType::Dir,
241            BLOCK_SIZE as u64,
242            1,
243        ))
244    }
245
246    fn parent(&self) -> Option<VfsNodeRef> {
247        self.0
248            .open_dir("..")
249            .map_or(None, |dir| Some(FatFileSystem::new_dir(dir)))
250    }
251
252    fn lookup(self: Arc<Self>, path: &str) -> VfsResult<VfsNodeRef> {
253        debug!("lookup at axfatfs: {}", path);
254        let path = path.trim_matches('/');
255        if path.is_empty() || path == "." {
256            return Ok(self.clone());
257        }
258        if let Some(rest) = path.strip_prefix("./") {
259            return self.lookup(rest);
260        }
261
262        // TODO: use `axfatfs::Dir::find_entry`, but it's not public.
263        if let Ok(file) = self.0.open_file(path) {
264            Ok(FatFileSystem::new_file(file))
265        } else if let Ok(dir) = self.0.open_dir(path) {
266            Ok(FatFileSystem::new_dir(dir))
267        } else {
268            Err(VfsError::NotFound)
269        }
270    }
271
272    fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult {
273        debug!("create {:?} at axfatfs: {}", ty, path);
274        let path = path.trim_matches('/');
275        if path.is_empty() || path == "." {
276            return Ok(());
277        }
278        if let Some(rest) = path.strip_prefix("./") {
279            return self.create(rest, ty);
280        }
281
282        match ty {
283            VfsNodeType::File => {
284                self.0.create_file(path).map_err(as_vfs_err)?;
285                Ok(())
286            }
287            VfsNodeType::Dir => {
288                self.0.create_dir(path).map_err(as_vfs_err)?;
289                Ok(())
290            }
291            _ => Err(VfsError::Unsupported),
292        }
293    }
294
295    fn remove(&self, path: &str) -> VfsResult {
296        debug!("remove at axfatfs: {}", path);
297        let path = path.trim_matches('/');
298        assert!(!path.is_empty()); // already check at `root.rs`
299        if let Some(rest) = path.strip_prefix("./") {
300            return self.remove(rest);
301        }
302        self.0.remove(path).map_err(as_vfs_err)
303    }
304
305    fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult<usize> {
306        let mut iter = self.0.iter().skip(start_idx);
307        for (i, out_entry) in dirents.iter_mut().enumerate() {
308            let x = iter.next();
309            match x {
310                Some(Ok(entry)) => {
311                    let ty = if entry.is_dir() {
312                        VfsNodeType::Dir
313                    } else if entry.is_file() {
314                        VfsNodeType::File
315                    } else {
316                        unreachable!()
317                    };
318                    *out_entry = VfsDirEntry::new(&entry.file_name(), ty);
319                }
320                _ => return Ok(i),
321            }
322        }
323        Ok(dirents.len())
324    }
325
326    fn rename(&self, src_path: &str, dst_path: &str) -> VfsResult {
327        // `src_path` and `dst_path` should in the same mounted fs
328        debug!(
329            "rename at axfatfs, src_path: {}, dst_path: {}",
330            src_path, dst_path
331        );
332
333        self.0
334            .rename(src_path, &self.0, dst_path)
335            .map_err(as_vfs_err)
336    }
337}
338
339impl VfsOps for FatFileSystem {
340    fn root_dir(&self) -> VfsNodeRef {
341        self.root_dir
342            .get_or_init(|| {
343                debug!("Creating root directory for FAT filesystem");
344                let root_dir = self.inner.root_dir();
345                debug!("Successfully got root directory from FAT filesystem");
346                Self::new_dir(root_dir)
347            })
348            .clone()
349    }
350}
351
352impl axfatfs::IoBase for Disk {
353    type Error = ();
354}
355
356impl Read for Disk {
357    fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, Self::Error> {
358        let mut read_len = 0;
359        while !buf.is_empty() {
360            match self.read_one(buf) {
361                Ok(0) => break,
362                Ok(n) => {
363                    let tmp = buf;
364                    buf = &mut tmp[n..];
365                    read_len += n;
366                }
367                Err(_) => return Err(()),
368            }
369        }
370        Ok(read_len)
371    }
372}
373
374impl Write for Disk {
375    fn write(&mut self, mut buf: &[u8]) -> Result<usize, Self::Error> {
376        let mut write_len = 0;
377        while !buf.is_empty() {
378            match self.write_one(buf) {
379                Ok(0) => break,
380                Ok(n) => {
381                    buf = &buf[n..];
382                    write_len += n;
383                }
384                Err(_) => return Err(()),
385            }
386        }
387        Ok(write_len)
388    }
389    fn flush(&mut self) -> Result<(), Self::Error> {
390        Ok(())
391    }
392}
393
394impl Seek for Disk {
395    fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
396        let size = self.size();
397        let new_pos = match pos {
398            SeekFrom::Start(pos) => Some(pos),
399            SeekFrom::Current(off) => self.position().checked_add_signed(off),
400            SeekFrom::End(off) => size.checked_add_signed(off),
401        }
402        .ok_or(())?;
403        if new_pos > size {
404            warn!("Seek beyond the end of the block device");
405        }
406        self.set_position(new_pos);
407        Ok(new_pos)
408    }
409}
410
411const fn as_vfs_err(err: axfatfs::Error<()>) -> VfsError {
412    use axfatfs::Error::*;
413    match err {
414        AlreadyExists => VfsError::AlreadyExists,
415        CorruptedFileSystem => VfsError::InvalidData,
416        DirectoryIsNotEmpty => VfsError::DirectoryNotEmpty,
417        InvalidInput | InvalidFileNameLength | UnsupportedFileNameCharacter => {
418            VfsError::InvalidInput
419        }
420        NotEnoughSpace => VfsError::StorageFull,
421        NotFound => VfsError::NotFound,
422        UnexpectedEof => VfsError::UnexpectedEof,
423        WriteZero => VfsError::WriteZero,
424        Io(_) => VfsError::Io,
425        _ => VfsError::Io,
426    }
427}