ahrightos-fs 0.1.0

Portable filesystem, block-device, and IPC primitives for the AhRightOS teaching kernel.
Documentation
use crate::file::File;
use alloc::sync::Arc;
use alloc::vec::Vec;
use spin::Mutex;

pub type FileDescriptor = usize;

pub const STDIN: FileDescriptor = 0;
pub const STDOUT: FileDescriptor = 1;
pub const STDERR: FileDescriptor = 2;
pub const FILE_DESCRIPTOR_LIMIT: usize = 1024;

pub struct FdEntry {
    file: Arc<Mutex<dyn File>>,
    flags: u32,
}

impl FdEntry {
    pub fn new(file: Arc<Mutex<dyn File>>) -> Self {
        Self { file, flags: 0 }
    }

    pub fn file(&self) -> Arc<Mutex<dyn File>> {
        self.file.clone()
    }

    pub fn flags(&self) -> u32 {
        self.flags
    }
}

pub struct FileDescriptorTable {
    entries: Vec<Option<FdEntry>>,
    next_fd: FileDescriptor,
}

impl FileDescriptorTable {
    pub fn new() -> Self {
        let entries = alloc::vec![None, None, None];
        Self {
            entries,
            next_fd: 3,
        }
    }

    pub fn with_stdio(
        stdin: Arc<Mutex<dyn File>>,
        stdout: Arc<Mutex<dyn File>>,
        stderr: Arc<Mutex<dyn File>>,
    ) -> Self {
        let mut table = Self {
            entries: Vec::with_capacity(16),
            next_fd: 3,
        };
        table.entries.push(Some(FdEntry::new(stdin)));
        table.entries.push(Some(FdEntry::new(stdout)));
        table.entries.push(Some(FdEntry::new(stderr)));
        table
    }

    pub fn alloc(&mut self, file: Arc<Mutex<dyn File>>) -> Option<FileDescriptor> {
        for (i, slot) in self.entries.iter_mut().enumerate() {
            if slot.is_none() && i >= 3 {
                *slot = Some(FdEntry::new(file));
                self.next_fd = i + 1;
                return Some(i);
            }
        }
        if self.entries.len() >= FILE_DESCRIPTOR_LIMIT {
            return None;
        }
        let fd = self.entries.len();
        self.entries.push(Some(FdEntry::new(file)));
        self.next_fd = fd + 1;
        Some(fd)
    }

    pub fn dealloc(&mut self, fd: FileDescriptor) -> bool {
        if fd >= 3 && fd < self.entries.len() && self.entries[fd].is_some() {
            self.entries[fd] = None;
            if fd < self.next_fd {
                self.next_fd = fd;
            }
            return true;
        }
        false
    }

    pub fn get(&self, fd: FileDescriptor) -> Option<Arc<Mutex<dyn File>>> {
        self.entries.get(fd)?.as_ref().map(|entry| entry.file())
    }

    pub fn is_valid(&self, fd: FileDescriptor) -> bool {
        self.get(fd).is_some()
    }

    pub fn dup2(
        &mut self,
        old_fd: FileDescriptor,
        new_fd: FileDescriptor,
    ) -> Option<FileDescriptor> {
        if old_fd == new_fd {
            return if self.is_valid(old_fd) {
                Some(new_fd)
            } else {
                None
            };
        }
        if new_fd >= FILE_DESCRIPTOR_LIMIT {
            return None;
        }
        let file = self.get(old_fd)?;
        while self.entries.len() <= new_fd {
            self.entries.push(None);
        }
        self.entries[new_fd] = Some(FdEntry { file, flags: 0 });
        Some(new_fd)
    }

    pub fn count(&self) -> usize {
        self.entries.iter().filter(|entry| entry.is_some()).count()
    }

    pub fn capacity(&self) -> usize {
        self.entries.len()
    }
}

impl Default for FileDescriptorTable {
    fn default() -> Self {
        Self::new()
    }
}