kernel/fs/
mod.rs

1mod fat;
2pub mod mapping;
3mod mbr;
4pub mod path;
5
6use core::ops;
7
8use alloc::{boxed::Box, string::String, sync::Arc, vec, vec::Vec};
9use kernel_user_link::file::{BlockingMode, DirEntry, FileStat, FileType, OpenOptions};
10use mapping::MappingError;
11use path::PathBuf;
12use tracing::info;
13
14use crate::{
15    devices::{
16        ide::{self, IdeDeviceIndex, IdeDeviceType},
17        Device, DEVICES_FILESYSTEM_CLUSTER_MAGIC,
18    },
19    sync::{once::OnceLock, spin::mutex::Mutex},
20};
21
22use self::{
23    mbr::Mbr,
24    path::{Component, Path},
25};
26
27/// This is not used at all, just an indicator in [`Directory::fetch_entries`]
28pub(crate) const ANOTHER_FILESYSTEM_MAPPING_INODE_MAGIC: u64 = 0xf11356573e;
29pub(crate) const NO_PARENT_DIR_SECTOR: u64 = 0xFFFF_FFFF_FFFF_FFFF;
30
31static EMPTY_FILESYSTEM: OnceLock<Arc<EmptyFileSystem>> = OnceLock::new();
32
33pub fn empty_filesystem() -> Arc<EmptyFileSystem> {
34    EMPTY_FILESYSTEM
35        .get_or_init(|| Arc::new(EmptyFileSystem))
36        .clone()
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct FileAttributes(pub u8);
41
42#[allow(dead_code)]
43impl FileAttributes {
44    pub const EMPTY: FileAttributes = FileAttributes(0);
45    pub const READ_ONLY: FileAttributes = FileAttributes(0b0000_0001);
46    pub const HIDDEN: FileAttributes = FileAttributes(0b0000_0010);
47    pub const SYSTEM: FileAttributes = FileAttributes(0b0000_0100);
48    pub const VOLUME_LABEL: FileAttributes = FileAttributes(0b0000_1000);
49    pub const DIRECTORY: FileAttributes = FileAttributes(0b0001_0000);
50    pub const ARCHIVE: FileAttributes = FileAttributes(0b0010_0000);
51
52    pub fn read_only(self) -> bool {
53        self.0 & Self::READ_ONLY.0 != 0
54    }
55
56    pub fn hidden(self) -> bool {
57        self.0 & Self::HIDDEN.0 != 0
58    }
59
60    pub fn system(self) -> bool {
61        self.0 & Self::SYSTEM.0 != 0
62    }
63
64    pub fn volume_label(self) -> bool {
65        self.0 & Self::VOLUME_LABEL.0 != 0
66    }
67
68    pub fn directory(self) -> bool {
69        self.0 & Self::DIRECTORY.0 != 0
70    }
71
72    pub fn archive(self) -> bool {
73        self.0 & Self::ARCHIVE.0 != 0
74    }
75
76    fn contains(&self, other: FileAttributes) -> bool {
77        self.0 & other.0 != 0
78    }
79}
80
81impl ops::BitOr for FileAttributes {
82    type Output = Self;
83
84    fn bitor(self, rhs: Self) -> Self::Output {
85        FileAttributes(self.0 | rhs.0)
86    }
87}
88
89impl ops::BitOrAssign for FileAttributes {
90    fn bitor_assign(&mut self, rhs: Self) {
91        self.0 |= rhs.0;
92    }
93}
94
95impl ops::BitAnd for FileAttributes {
96    type Output = Self;
97
98    fn bitand(self, rhs: Self) -> Self::Output {
99        FileAttributes(self.0 & rhs.0)
100    }
101}
102
103#[derive(Debug, Clone)]
104pub struct BaseNode {
105    name: String,
106    attributes: FileAttributes,
107    start_cluster: u64,
108    parent_dir_sector: u64,
109    /// The position of this file in the parent directory
110    /// the size of the sector shouldn't exceed 16 bits
111    /// this is element wise and not byte wise
112    parent_dir_index: u16,
113}
114
115impl BaseNode {
116    pub fn name(&self) -> &str {
117        &self.name
118    }
119
120    pub fn start_cluster(&self) -> u64 {
121        self.start_cluster
122    }
123
124    #[allow(dead_code)]
125    pub fn attributes(&self) -> FileAttributes {
126        self.attributes
127    }
128
129    #[allow(dead_code)]
130    pub fn parent_dir_sector(&self) -> u64 {
131        self.parent_dir_sector
132    }
133
134    #[allow(dead_code)]
135    pub fn parent_dir_index(&self) -> u16 {
136        self.parent_dir_index
137    }
138}
139
140#[derive(Debug, Clone)]
141pub struct FileNode {
142    base: BaseNode,
143    size: u64,
144    device: Option<Arc<dyn Device>>,
145}
146
147impl FileNode {
148    pub fn new_file(
149        name: String,
150        attributes: FileAttributes,
151        start_cluster: u64,
152        size: u64,
153        parent_dir_sector: u64,
154        parent_dir_index: u16,
155    ) -> Self {
156        assert!(!attributes.directory());
157        Self {
158            base: BaseNode {
159                name,
160                attributes,
161                start_cluster,
162                parent_dir_sector,
163                parent_dir_index,
164            },
165            size,
166            device: None,
167        }
168    }
169
170    pub fn new_device(name: String, attributes: FileAttributes, device: Arc<dyn Device>) -> Self {
171        assert!(!attributes.directory());
172        Self {
173            base: BaseNode {
174                name,
175                attributes,
176                start_cluster: DEVICES_FILESYSTEM_CLUSTER_MAGIC,
177                parent_dir_sector: NO_PARENT_DIR_SECTOR,
178                parent_dir_index: 0,
179            },
180            size: 0,
181            device: Some(device),
182        }
183    }
184
185    pub fn size(&self) -> u64 {
186        self.size
187    }
188
189    pub(self) fn set_size(&mut self, size: u64) {
190        self.size = size;
191    }
192
193    pub fn try_open_device(&mut self) -> Result<(), FileSystemError> {
194        if let Some(device) = self.device.take() {
195            self.device = Some(device.try_create().unwrap_or(Ok(device))?);
196        }
197
198        Ok(())
199    }
200}
201
202impl Drop for FileNode {
203    fn drop(&mut self) {
204        if let Some(device) = self.device.take() {
205            device.close().expect("Failed to close device");
206        }
207    }
208}
209
210#[derive(Debug, Clone)]
211pub struct DirectoryNode {
212    base: BaseNode,
213}
214
215impl DirectoryNode {
216    pub fn without_parent(name: String, attributes: FileAttributes, start_cluster: u64) -> Self {
217        Self::new(name, attributes, start_cluster, NO_PARENT_DIR_SECTOR, 0)
218    }
219
220    pub fn new(
221        name: String,
222        attributes: FileAttributes,
223        start_cluster: u64,
224        parent_dir_sector: u64,
225        parent_dir_index: u16,
226    ) -> Self {
227        assert!(attributes.directory());
228        Self {
229            base: BaseNode {
230                name,
231                attributes,
232                start_cluster,
233                parent_dir_sector,
234                parent_dir_index,
235            },
236        }
237    }
238}
239
240/// A node of the filesystem, it can be anything, a file, a device or a directory
241#[derive(Debug, Clone)]
242pub enum Node {
243    File(FileNode),
244    Directory(DirectoryNode),
245}
246
247impl From<FileNode> for Node {
248    fn from(file: FileNode) -> Self {
249        Self::File(file)
250    }
251}
252
253impl From<DirectoryNode> for Node {
254    fn from(dir: DirectoryNode) -> Self {
255        Self::Directory(dir)
256    }
257}
258
259impl Node {
260    pub fn new(
261        name: String,
262        attributes: FileAttributes,
263        start_cluster: u64,
264        size: u64,
265        parent_dir_sector: u64,
266        parent_dir_index: u16,
267    ) -> Self {
268        if attributes.directory() {
269            Self::Directory(DirectoryNode::new(
270                name,
271                attributes,
272                start_cluster,
273                parent_dir_sector,
274                parent_dir_index,
275            ))
276        } else {
277            Self::File(FileNode::new_file(
278                name,
279                attributes,
280                start_cluster,
281                size,
282                parent_dir_sector,
283                parent_dir_index,
284            ))
285        }
286    }
287
288    pub fn size(&self) -> u64 {
289        match self {
290            Self::File(file) => file.size,
291            Self::Directory(_) => 0,
292        }
293    }
294
295    pub fn name(&self) -> &str {
296        match self {
297            Self::File(file) => &file.name,
298            Self::Directory(dir) => &dir.name,
299        }
300    }
301
302    pub fn is_dir(&self) -> bool {
303        matches!(self, Self::Directory(_))
304    }
305
306    pub fn into_dir(self) -> Result<DirectoryNode, FileSystemError> {
307        match self {
308            Self::Directory(dir) => Ok(dir),
309            Self::File(_) => Err(FileSystemError::IsNotDirectory),
310        }
311    }
312
313    pub fn into_file(self) -> Result<FileNode, FileSystemError> {
314        match self {
315            Self::File(file) => Ok(file),
316            Self::Directory(_) => Err(FileSystemError::IsDirectory),
317        }
318    }
319
320    #[allow(dead_code)]
321    pub fn attributes(&self) -> FileAttributes {
322        match self {
323            Self::File(file) => file.attributes,
324            Self::Directory(dir) => dir.attributes,
325        }
326    }
327
328    pub fn as_file_stat(&self) -> FileStat {
329        FileStat {
330            size: self.size(),
331            file_type: match self {
332                Self::File(_) => FileType::File,
333                Self::Directory(_) => FileType::Directory,
334            },
335        }
336    }
337
338    pub fn try_open_device(&mut self) -> Result<(), FileSystemError> {
339        if let Self::File(file) = self {
340            file.try_open_device()?;
341        }
342
343        Ok(())
344    }
345}
346
347impl ops::Deref for FileNode {
348    type Target = BaseNode;
349
350    fn deref(&self) -> &Self::Target {
351        &self.base
352    }
353}
354
355impl ops::DerefMut for FileNode {
356    fn deref_mut(&mut self) -> &mut Self::Target {
357        &mut self.base
358    }
359}
360
361impl ops::Deref for DirectoryNode {
362    type Target = BaseNode;
363
364    fn deref(&self) -> &Self::Target {
365        &self.base
366    }
367}
368
369impl ops::DerefMut for DirectoryNode {
370    fn deref_mut(&mut self) -> &mut Self::Target {
371        &mut self.base
372    }
373}
374
375impl ops::Deref for Node {
376    type Target = BaseNode;
377
378    fn deref(&self) -> &Self::Target {
379        match self {
380            Self::File(file) => file,
381            Self::Directory(dir) => dir,
382        }
383    }
384}
385
386impl ops::DerefMut for Node {
387    fn deref_mut(&mut self) -> &mut Self::Target {
388        match self {
389            Self::File(file) => file,
390            Self::Directory(dir) => dir,
391        }
392    }
393}
394
395// This is some sort of cache or extra metadata the filesystem
396// use to help implement the filesystem and improve performance
397#[derive(Debug, Default)]
398pub struct AccessHelper {
399    current_cluster: u64,
400    cluster_index: u64,
401}
402
403pub enum DirTraverse {
404    Continue,
405    Stop,
406}
407
408/// A filesystem trait, this is the main interface to interact with the filesystem
409/// it is used to open files, directories, read and write files, etc.
410pub trait FileSystem: Send + Sync {
411    /// Open the root directory of the filesystem
412    fn open_root(&self) -> Result<DirectoryNode, FileSystemError>;
413
414    /// Read the directory entries in the `inode`, and call the handler for each entry
415    /// The `handler` should return `DirTraverse::Stop` to stop the traversal
416    fn read_dir(
417        &self,
418        inode: &DirectoryNode,
419        handler: &mut dyn FnMut(Node) -> DirTraverse,
420    ) -> Result<(), FileSystemError>;
421
422    /// Traverse the directory in the `inode` and return the entry with the name `matcher`
423    /// Most of the time, no need to implement this, as it is already implemented in the default
424    /// using [`FileSystem::read_dir`]
425    fn traverse_dir(&self, inode: &DirectoryNode, matcher: &str) -> Result<Node, FileSystemError> {
426        let mut entry = None;
427        self.read_dir(inode, &mut |inode| {
428            if inode.name() == matcher {
429                entry = Some(inode);
430                DirTraverse::Stop
431            } else {
432                DirTraverse::Continue
433            }
434        })?;
435        entry.ok_or(FileSystemError::FileNotFound)
436    }
437
438    /// Create a new entry in the `parent` directory with the `name` and `attributes`
439    /// This could be a file or a directory, the `attributes` should specify the type
440    fn create_node(
441        &self,
442        _parent: &DirectoryNode,
443        _name: &str,
444        _attributes: FileAttributes,
445    ) -> Result<Node, FileSystemError> {
446        Err(FileSystemError::OperationNotSupported)
447    }
448
449    /// Read the file in the `inode` at the `position` and put the data in `buf`
450    /// The `access_helper` is used to store some extra metadata to help the filesystem
451    /// manage the caches or any extra data it needs.
452    fn read_file(
453        &self,
454        inode: &FileNode,
455        position: u64,
456        buf: &mut [u8],
457        _access_helper: &mut AccessHelper,
458    ) -> Result<u64, FileSystemError> {
459        if let Some(device) = &inode.device {
460            assert_eq!(inode.start_cluster, DEVICES_FILESYSTEM_CLUSTER_MAGIC);
461            device.read(position, buf)
462        } else {
463            Err(FileSystemError::ReadNotSupported)
464        }
465    }
466
467    /// Write the file in the `inode` at the `position` with the data in `buf`
468    /// The `access_helper` is used to store some extra metadata to help the filesystem
469    /// manage the caches or any extra data it needs.
470    fn write_file(
471        &self,
472        inode: &mut FileNode,
473        position: u64,
474        buf: &[u8],
475        _access_helper: &mut AccessHelper,
476    ) -> Result<u64, FileSystemError> {
477        if let Some(device) = &inode.device {
478            assert_eq!(inode.start_cluster, DEVICES_FILESYSTEM_CLUSTER_MAGIC);
479            device.write(position, buf)
480        } else {
481            Err(FileSystemError::WriteNotSupported)
482        }
483    }
484
485    /// Tells the filesystem to flush the content of this file to disk or to the backing store
486    /// if it needs to
487    fn flush_file(
488        &self,
489        _inode: &mut FileNode,
490        _access_helper: &mut AccessHelper,
491    ) -> Result<(), FileSystemError> {
492        Err(FileSystemError::WriteNotSupported)
493    }
494
495    /// Close the file in the `inode`, this is called when the file is dropped
496    /// The `access_helper` is used to store some extra metadata to help the filesystem
497    /// manage the caches or any extra data it needs.
498    fn close_file(
499        &self,
500        _inode: &FileNode,
501        _access_helper: AccessHelper,
502    ) -> Result<(), FileSystemError> {
503        Ok(())
504    }
505
506    /// Set the size of the file in the `inode` to `size`, this is used to truncate the file
507    /// or to extend it
508    fn set_file_size(&self, inode: &mut FileNode, size: u64) -> Result<(), FileSystemError> {
509        if let Some(device) = &inode.device {
510            assert_eq!(inode.start_cluster, DEVICES_FILESYSTEM_CLUSTER_MAGIC);
511            device.set_size(size)
512        } else {
513            Err(FileSystemError::WriteNotSupported)
514        }
515    }
516
517    /// Unmount the filesystem, this is called before the filesystem is dropped
518    /// The reason we use this is that we can't force `Drop` to be implemented
519    /// for `Arc<dyn FileSystem>`, so we have this instead
520    fn unmount(self: Arc<Self>) {}
521}
522
523pub struct EmptyFileSystem;
524
525impl FileSystem for EmptyFileSystem {
526    fn open_root(&self) -> Result<DirectoryNode, FileSystemError> {
527        Err(FileSystemError::FileNotFound)
528    }
529
530    fn read_dir(
531        &self,
532        _inode: &DirectoryNode,
533        _handler: &mut dyn FnMut(Node) -> DirTraverse,
534    ) -> Result<(), FileSystemError> {
535        Err(FileSystemError::FileNotFound)
536    }
537}
538
539#[derive(Debug)]
540pub enum FileSystemError {
541    PartitionTableNotFound,
542    DeviceNotFound,
543    DiskReadError { sector: u64, error: ide::IdeError },
544    FatError(fat::FatError),
545    FileNotFound,
546    InvalidPath,
547    MustBeAbsolute,
548    IsNotDirectory,
549    IsDirectory,
550    ReadNotSupported,
551    WriteNotSupported,
552    OperationNotSupported,
553    CouldNotSetFileLength,
554    EndOfFile,
555    BufferNotLargeEnough(usize),
556    AlreadyExists,
557    MappingError(MappingError),
558}
559
560/// Loads the hard disk specified in the argument
561/// it will load the first partition (MBR) if any, otherwise it will treat the whole disk
562/// as one partition
563///
564/// Creates a new filesystem mapping for `/` and the filesystem found
565pub fn create_disk_mapping(hard_disk_index: usize) -> Result<(), FileSystemError> {
566    let ide_index = IdeDeviceIndex {
567        ty: IdeDeviceType::Ata,
568        index: hard_disk_index,
569    };
570
571    let device = ide::get_ide_device(ide_index).ok_or(FileSystemError::DeviceNotFound)?;
572
573    let mbr = Mbr::try_create_from_disk(&device)?;
574
575    // load the first partition for now
576    let first_partition = &mbr.partition_table[0];
577    let filesystem = fat::load_fat_filesystem(
578        device,
579        first_partition.start_lba,
580        first_partition.size_in_sectors,
581    )?;
582    info!(
583        "Mapping / to FAT filesystem {:?} ({:?}), partition_type: 0x{:02X}",
584        filesystem.volume_label(),
585        filesystem.fat_type(),
586        first_partition.partition_type
587    );
588    mapping::mount("/", Arc::new(Mutex::new(filesystem)))?;
589
590    Ok(())
591}
592
593pub fn unmount_all() {
594    // unmount all filesystems
595    mapping::unmount_all();
596}
597
598/// Open the inode of a path, this include directories and files.
599///
600/// This function must be called with an absolute path. Otherwise it will return [`FileSystemError::MustBeAbsolute`].
601pub(crate) fn open_inode<P: AsRef<Path>>(
602    path: P,
603) -> Result<(PathBuf, Arc<dyn FileSystem>, Node), FileSystemError> {
604    if !path.as_ref().is_absolute() {
605        // this is an internal kernel only result, this function must be called with an absolute path
606        return Err(FileSystemError::MustBeAbsolute);
607    }
608
609    let (mut canonical_path, remaining, mut mapping_node) = mapping::get_mapping(path.as_ref())?;
610    let mut filesystem = mapping_node.filesystem();
611
612    let opening_dir = path.as_ref().has_last_separator();
613    let mut remaining_components = remaining.components().peekable();
614
615    let mut dir = filesystem.open_root()?;
616    if remaining.is_root() || remaining.is_empty() {
617        return Ok((canonical_path, filesystem, dir.into()));
618    }
619
620    // used to know when to switch to different mappings
621    let mut children_after_mapping = 0;
622    while let Some(component) = remaining_components.next() {
623        let name = match component {
624            Component::RootDir | Component::CurDir => continue,
625            Component::Normal("") => continue,
626            Component::ParentDir => {
627                if children_after_mapping == 0 {
628                    // reached the end, i.e. this exceeded the filesystem mapping
629                    // we should get another mapping
630                    let parent = mapping_node.parent();
631                    if let Some(parent) = parent {
632                        mapping_node = parent.clone();
633                        assert!(canonical_path.pop(), "must have parent");
634                        filesystem = mapping_node.filesystem();
635                        dir = filesystem.open_root()?;
636                    }
637                } else {
638                    children_after_mapping -= 1;
639                    assert!(canonical_path.pop(), "must have parent");
640                }
641                continue;
642            }
643            Component::Normal(name) => name,
644        };
645        // if we are still at the end of the mapping
646        if children_after_mapping == 0 {
647            if let Some(child_mapping) = mapping_node.try_find_child(name) {
648                mapping_node = child_mapping;
649                canonical_path.push(name);
650                filesystem = mapping_node.filesystem();
651                children_after_mapping = 0;
652                dir = filesystem.open_root()?;
653                continue;
654            }
655        }
656
657        children_after_mapping += 1;
658        canonical_path.push(name);
659
660        let mut entry = filesystem.traverse_dir(&dir, name)?;
661
662        if remaining_components.peek().is_some() {
663            if let Node::Directory(dir_node) = entry {
664                dir = dir_node;
665            } else {
666                return Err(FileSystemError::IsNotDirectory);
667            }
668        } else {
669            return if opening_dir {
670                if entry.is_dir() {
671                    Ok((canonical_path, filesystem, entry))
672                } else {
673                    Err(FileSystemError::IsNotDirectory)
674                }
675            } else {
676                // open the device if it is a device
677                entry.try_open_device()?;
678                Ok((canonical_path, filesystem, entry))
679            };
680        }
681    }
682
683    // should never reach here, but for some reason, if we got some path length but no components
684    // we should just return the root entry
685    Ok((canonical_path, filesystem, dir.into()))
686}
687
688#[derive(Debug, Clone, Copy, PartialEq, Eq)]
689pub struct FileAccess {
690    read: bool,
691    write: bool,
692}
693
694impl FileAccess {
695    pub const READ: Self = Self {
696        read: true,
697        write: false,
698    };
699    pub const WRITE: Self = Self {
700        read: false,
701        write: true,
702    };
703
704    fn new(read: bool, write: bool) -> Self {
705        Self { read, write }
706    }
707
708    fn is_read(&self) -> bool {
709        self.read
710    }
711
712    fn is_write(&self) -> bool {
713        self.write
714    }
715}
716
717impl ops::BitOr for FileAccess {
718    type Output = Self;
719
720    fn bitor(self, rhs: Self) -> Self::Output {
721        Self {
722            read: self.read || rhs.read,
723            write: self.write || rhs.write,
724        }
725    }
726}
727
728/// A handle to a file, it has the inode which controls the properties of the node in the filesystem
729pub struct File {
730    filesystem: Arc<dyn FileSystem>,
731    path: Box<Path>,
732    inode: FileNode,
733    position: u64,
734    is_terminal: bool,
735    blocking_mode: BlockingMode,
736    access_helper: AccessHelper,
737    file_access: FileAccess,
738}
739
740/// A handle to a directory, it has the inode which controls the properties of the node in the filesystem
741#[allow(dead_code)]
742pub struct Directory {
743    inode: DirectoryNode,
744    path: Box<Path>,
745    position: u64,
746    dir_entries: Option<Vec<Node>>,
747    filesystem: Arc<dyn FileSystem>,
748}
749
750/// A node in the filesystem, can be a file or a directory
751#[allow(dead_code)]
752#[repr(u8)]
753pub enum FilesystemNode {
754    File(File),
755    Directory(Directory),
756}
757
758#[allow(dead_code)]
759impl File {
760    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, FileSystemError> {
761        Self::open_blocking(path, BlockingMode::None, OpenOptions::default())
762    }
763
764    pub fn open_blocking<P: AsRef<Path>>(
765        path: P,
766        blocking_mode: BlockingMode,
767        open_options: OpenOptions,
768    ) -> Result<Self, FileSystemError> {
769        let (canonical_path, mut node, filesystem) = match open_inode(path.as_ref()) {
770            Ok((canonical_path, filesystem, inode)) => {
771                if open_options.is_create_new() {
772                    return Err(FileSystemError::AlreadyExists);
773                }
774
775                (canonical_path, inode.into_file()?, filesystem)
776            }
777            Err(FileSystemError::FileNotFound)
778                if open_options.is_create() || open_options.is_create_new() =>
779            {
780                let path = path.as_ref();
781                let (canonical_path, filesystem, parent_inode) =
782                    open_inode(path.parent().unwrap())?;
783                let filename = path.file_name().unwrap();
784                if filename == "." || filename == ".." || filename == "/" {
785                    return Err(FileSystemError::InvalidPath);
786                }
787                let node = filesystem.create_node(
788                    &parent_inode.into_dir()?,
789                    filename,
790                    FileAttributes::EMPTY,
791                )?;
792                (
793                    canonical_path,
794                    node.into_file()
795                        .expect("This should be a valid file, we created it"),
796                    filesystem,
797                )
798            }
799            Err(e) => return Err(e),
800        };
801
802        if open_options.is_truncate() {
803            if open_options.is_write() {
804                filesystem.set_file_size(&mut node, 0)?;
805            } else {
806                return Err(FileSystemError::WriteNotSupported);
807            }
808        }
809
810        let pos = if open_options.is_append() {
811            node.size()
812        } else {
813            0
814        };
815
816        let access = FileAccess::new(open_options.is_read(), open_options.is_write());
817
818        Self::from_inode(node, canonical_path, filesystem, pos, blocking_mode, access)
819    }
820
821    pub fn from_inode<P: AsRef<Path>>(
822        inode: FileNode,
823        path: P,
824        filesystem: Arc<dyn FileSystem>,
825        position: u64,
826        blocking_mode: BlockingMode,
827        file_access: FileAccess,
828    ) -> Result<Self, FileSystemError> {
829        Ok(Self {
830            filesystem,
831            path: path.as_ref().into(),
832            inode,
833            position,
834            is_terminal: false,
835            blocking_mode,
836            access_helper: AccessHelper::default(),
837            file_access,
838        })
839    }
840
841    pub fn read(&mut self, buf: &mut [u8]) -> Result<u64, FileSystemError> {
842        if !self.file_access.is_read() {
843            return Err(FileSystemError::ReadNotSupported);
844        }
845
846        let count = match self.blocking_mode {
847            BlockingMode::None => self.filesystem.read_file(
848                &self.inode,
849                self.position,
850                buf,
851                &mut self.access_helper,
852            )?,
853            BlockingMode::Line => {
854                // read until \n or \0
855                let mut i = 0;
856                loop {
857                    let mut char_buf = 0;
858                    let read_byte = self.filesystem.read_file(
859                        &self.inode,
860                        self.position,
861                        core::slice::from_mut(&mut char_buf),
862                        &mut self.access_helper,
863                    );
864
865                    let read_byte = match read_byte {
866                        Ok(read_byte) => read_byte,
867                        Err(FileSystemError::EndOfFile) => {
868                            // if we reached the end of the file, we return i
869                            return Ok(i as u64);
870                        }
871                        Err(e) => return Err(e),
872                    };
873
874                    // only put if we can, otherwise, eat the byte and continue
875                    if read_byte == 1 {
876                        if i < buf.len() {
877                            buf[i] = char_buf;
878                            i += 1;
879                        }
880                        if char_buf == b'\n' || char_buf == b'\0' {
881                            break;
882                        }
883                    } else {
884                        // TODO: add IO waiting
885                        for _ in 0..100 {
886                            core::hint::spin_loop();
887                        }
888                    }
889                }
890                i as u64
891            }
892            BlockingMode::Block(size) => {
893                // TODO: support block size > 1
894                assert_eq!(size, 1, "Only block size 1 is supported");
895
896                // try to read until we have something
897                loop {
898                    let read_byte = self.filesystem.read_file(
899                        &self.inode,
900                        self.position,
901                        buf,
902                        &mut self.access_helper,
903                    );
904
905                    let read_byte = match read_byte {
906                        Ok(read_byte) => read_byte,
907                        Err(FileSystemError::EndOfFile) => {
908                            // if we reached the end of the file, we return 0
909                            break 0;
910                        }
911                        Err(e) => return Err(e),
912                    };
913
914                    // only if the result is not 0, we can return
915                    if read_byte != 0 {
916                        break read_byte;
917                    }
918                    // otherwise we wait
919                    // TODO: add IO waiting
920                    for _ in 0..100 {
921                        core::hint::spin_loop();
922                    }
923                }
924            }
925        };
926
927        self.position += count;
928        Ok(count)
929    }
930
931    pub fn write(&mut self, buf: &[u8]) -> Result<u64, FileSystemError> {
932        if !self.file_access.is_write() {
933            return Err(FileSystemError::WriteNotSupported);
934        }
935
936        let written = self.filesystem.write_file(
937            &mut self.inode,
938            self.position,
939            buf,
940            &mut self.access_helper,
941        )?;
942        self.position += written;
943        Ok(written)
944    }
945
946    pub fn flush(&mut self) -> Result<(), FileSystemError> {
947        if !self.file_access.is_write() {
948            return Err(FileSystemError::WriteNotSupported);
949        }
950
951        self.filesystem
952            .flush_file(&mut self.inode, &mut self.access_helper)
953    }
954
955    pub fn seek(&mut self, position: u64) -> Result<(), FileSystemError> {
956        self.position = position;
957        Ok(())
958    }
959
960    pub fn filesize(&self) -> u64 {
961        self.inode.size()
962    }
963
964    pub fn path(&self) -> &Path {
965        &self.path
966    }
967
968    pub fn read_to_end(&mut self) -> Result<Vec<u8>, FileSystemError> {
969        let mut buf = vec![0; self.inode.size() as usize];
970        let mut position = 0;
971        loop {
972            let read = self.read(&mut buf[position..])?;
973            if read == 0 {
974                break;
975            }
976            position += read as usize;
977        }
978        Ok(buf)
979    }
980
981    pub fn is_blocking(&self) -> bool {
982        self.blocking_mode != BlockingMode::None
983    }
984
985    pub fn blocking_mode(&self) -> BlockingMode {
986        self.blocking_mode
987    }
988
989    pub fn set_blocking(&mut self, blocking_mode: BlockingMode) {
990        self.blocking_mode = blocking_mode;
991    }
992
993    pub fn is_terminal(&self) -> bool {
994        self.is_terminal
995    }
996
997    pub fn set_terminal(&mut self, is_terminal: bool) {
998        self.is_terminal = is_terminal;
999    }
1000
1001    pub fn size(&self) -> u64 {
1002        self.inode.size()
1003    }
1004
1005    pub fn current_position(&self) -> u64 {
1006        self.position
1007    }
1008
1009    pub fn set_size(&mut self, size: u64) -> Result<(), FileSystemError> {
1010        if !self.file_access.is_write() {
1011            return Err(FileSystemError::WriteNotSupported);
1012        }
1013
1014        self.filesystem.set_file_size(&mut self.inode, size)
1015    }
1016
1017    /// This is a move verbose method than `Clone::clone`, as I want it to be
1018    /// more explicit to the user that this is not a normal `clone` operation.
1019    pub fn clone_inherit(&self) -> Self {
1020        let s = Self {
1021            filesystem: self.filesystem.clone(),
1022            path: self.path.clone(),
1023            inode: self.inode.clone(),
1024            position: 0,
1025            is_terminal: self.is_terminal,
1026            blocking_mode: self.blocking_mode,
1027            access_helper: AccessHelper::default(),
1028            file_access: self.file_access,
1029        };
1030
1031        // inform the device of a clone operation
1032        if let Some(device) = s.inode.device.as_ref() {
1033            device
1034                .clone_device()
1035                // TODO: maybe use error handling instead
1036                .expect("Failed to clone device for file")
1037        }
1038
1039        s
1040    }
1041}
1042
1043impl Drop for File {
1044    fn drop(&mut self) {
1045        self.filesystem
1046            .close_file(&self.inode, core::mem::take(&mut self.access_helper))
1047            .expect("Failed to close file");
1048    }
1049}
1050
1051#[allow(dead_code)]
1052impl Directory {
1053    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, FileSystemError> {
1054        let (canonical_path, filesystem, inode) = open_inode(path.as_ref())?;
1055
1056        Self::from_inode(inode.into_dir()?, canonical_path, filesystem, 0)
1057    }
1058
1059    pub fn from_inode<P: AsRef<Path>>(
1060        inode: DirectoryNode,
1061        path: P,
1062        filesystem: Arc<dyn FileSystem>,
1063        position: u64,
1064    ) -> Result<Self, FileSystemError> {
1065        Ok(Self {
1066            path: path.as_ref().into(),
1067            inode,
1068            position,
1069            dir_entries: None,
1070            filesystem,
1071        })
1072    }
1073
1074    fn fetch_entries(&mut self) -> Result<(), FileSystemError> {
1075        if self.dir_entries.is_none() {
1076            let mut dir_entries = Vec::new();
1077            self.filesystem.read_dir(&self.inode, &mut |entry| {
1078                dir_entries.push(entry);
1079                DirTraverse::Continue
1080            })?;
1081            // add entries from the root mappings
1082            mapping::on_all_matching_mappings(&self.path, |path, _fs| {
1083                // only add path with one component
1084                if path.components().count() == 1 {
1085                    dir_entries.push(
1086                        DirectoryNode::without_parent(
1087                            path.components().next().unwrap().as_str().into(),
1088                            FileAttributes::DIRECTORY,
1089                            ANOTHER_FILESYSTEM_MAPPING_INODE_MAGIC,
1090                        )
1091                        .into(),
1092                    );
1093                }
1094            })?;
1095
1096            self.dir_entries = Some(dir_entries);
1097        }
1098
1099        Ok(())
1100    }
1101
1102    pub fn path(&self) -> &Path {
1103        &self.path
1104    }
1105
1106    pub fn create_node(
1107        &mut self,
1108        name: &str,
1109        attributes: FileAttributes,
1110    ) -> Result<FilesystemNode, FileSystemError> {
1111        let node = self.filesystem.create_node(&self.inode, name, attributes)?;
1112
1113        let path = self.path.join(name);
1114
1115        match node {
1116            Node::File(file) => Ok(File::from_inode(
1117                file,
1118                path,
1119                self.filesystem.clone(),
1120                0,
1121                BlockingMode::None,
1122                // TODO: for now, set it as readable and writable, find a better way to handle that
1123                FileAccess::READ | FileAccess::WRITE,
1124            )?
1125            .into()),
1126            Node::Directory(directory) => {
1127                Ok(Directory::from_inode(directory, path, self.filesystem.clone(), 0)?.into())
1128            }
1129        }
1130    }
1131
1132    pub fn read(&mut self, entries: &mut [DirEntry]) -> Result<usize, FileSystemError> {
1133        self.fetch_entries()?;
1134
1135        let dir_entries = self
1136            .dir_entries
1137            .as_ref()
1138            .expect("Entries must be initialized");
1139
1140        let mut i = 0;
1141        while i < entries.len() {
1142            if self.position >= dir_entries.len() as u64 {
1143                break;
1144            }
1145
1146            let entry = &dir_entries[self.position as usize];
1147            entries[i] = DirEntry {
1148                stat: entry.as_file_stat(),
1149                name: entry.name().into(),
1150            };
1151            i += 1;
1152            self.position += 1;
1153        }
1154
1155        Ok(i)
1156    }
1157}
1158
1159impl Clone for Directory {
1160    fn clone(&self) -> Self {
1161        Self {
1162            inode: self.inode.clone(),
1163            path: self.path.clone(),
1164            position: 0,
1165            dir_entries: None, // allow for refetch
1166            filesystem: self.filesystem.clone(),
1167        }
1168    }
1169}
1170
1171#[allow(dead_code)]
1172impl FilesystemNode {
1173    pub fn as_file(&self) -> Result<&File, FileSystemError> {
1174        match self {
1175            Self::File(file) => Ok(file),
1176            Self::Directory(_) => Err(FileSystemError::IsDirectory),
1177        }
1178    }
1179
1180    pub fn as_file_mut(&mut self) -> Result<&mut File, FileSystemError> {
1181        match self {
1182            Self::File(file) => Ok(file),
1183            Self::Directory(_) => Err(FileSystemError::IsDirectory),
1184        }
1185    }
1186
1187    pub fn as_dir_mut(&mut self) -> Result<&mut Directory, FileSystemError> {
1188        match self {
1189            Self::File(_) => Err(FileSystemError::IsNotDirectory),
1190            Self::Directory(dir) => Ok(dir),
1191        }
1192    }
1193}
1194
1195impl From<File> for FilesystemNode {
1196    fn from(file: File) -> Self {
1197        Self::File(file)
1198    }
1199}
1200
1201impl From<Directory> for FilesystemNode {
1202    fn from(dir: Directory) -> Self {
1203        Self::Directory(dir)
1204    }
1205}