2023-12-02 20:28:43 +02:00

501 lines
15 KiB
Rust

use core::{any::Any, cell::Cell, fmt, mem::MaybeUninit};
use alloc::{boxed::Box, sync::Arc};
use yggdrasil_abi::{
error::Error,
io::{DirectoryEntry, OpenOptions, SeekFrom},
};
use crate::{
device::{BlockDeviceWrapper, CharDeviceWrapper},
node::NodeRef,
traits::{Read, Seek, Write},
};
use self::{
device::{BlockFile, CharFile},
directory::DirectoryFile,
regular::RegularFile,
};
mod device;
mod directory;
mod regular;
pub enum DirectoryOpenPosition {
FromPhysical(u64),
FromCache,
}
pub type FileRef = Arc<File>;
// TODO some kind of a mutex instead?
pub enum File {
Directory(DirectoryFile),
Regular(RegularFile),
Block(BlockFile),
Char(CharFile),
}
impl File {
pub fn directory(node: NodeRef, position: DirectoryOpenPosition) -> Arc<Self> {
let position = Cell::new(position.into());
Arc::new(Self::Directory(DirectoryFile { node, position }))
}
pub fn regular(
node: NodeRef,
position: u64,
instance_data: Option<Box<dyn Any>>,
opts: OpenOptions,
) -> Arc<Self> {
let read = opts.contains(OpenOptions::READ);
let write = opts.contains(OpenOptions::WRITE);
Arc::new(Self::Regular(RegularFile {
node,
read,
write,
instance_data,
position: Cell::new(position),
}))
}
pub fn block(
device: BlockDeviceWrapper,
node: NodeRef,
opts: OpenOptions,
) -> Result<Arc<Self>, Error> {
let read = opts.contains(OpenOptions::READ);
let write = opts.contains(OpenOptions::WRITE);
if read && !device.is_readable() {
return Err(Error::InvalidOperation);
}
if write && !device.is_writable() {
return Err(Error::ReadOnly);
}
Ok(Arc::new(Self::Block(BlockFile {
device,
node,
position: Cell::new(0),
read,
write,
})))
}
pub fn char(
device: CharDeviceWrapper,
node: NodeRef,
opts: OpenOptions,
) -> Result<Arc<Self>, Error> {
let read = opts.contains(OpenOptions::READ);
let write = opts.contains(OpenOptions::WRITE);
if read && !device.is_readable() {
return Err(Error::InvalidOperation);
}
if write && !device.is_writable() {
return Err(Error::ReadOnly);
}
Ok(Arc::new(Self::Char(CharFile {
device,
node,
read,
write,
})))
}
pub fn read_dir(&self, entries: &mut [MaybeUninit<DirectoryEntry>]) -> Result<usize, Error> {
match self {
Self::Directory(dir) => dir.read_entries(entries),
_ => Err(Error::NotADirectory),
}
}
}
impl Read for File {
fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
match self {
Self::Regular(file) => file.read(buf),
Self::Block(file) => file.read(buf),
Self::Char(file) => file.read(buf),
Self::Directory(_) => Err(Error::IsADirectory),
}
}
}
impl Write for File {
fn write(&self, buf: &[u8]) -> Result<usize, Error> {
match self {
Self::Regular(file) => file.write(buf),
Self::Block(file) => file.write(buf),
Self::Char(file) => file.write(buf),
Self::Directory(_) => Err(Error::IsADirectory),
}
}
}
impl Seek for File {
fn tell(&self) -> Result<u64, Error> {
match self {
Self::Regular(file) => Ok(file.position.get()),
Self::Block(file) => Ok(file.position.get()),
Self::Char(_) => Err(Error::InvalidOperation),
Self::Directory(_) => Err(Error::IsADirectory),
}
}
fn seek(&self, from: SeekFrom) -> Result<u64, Error> {
match self {
Self::Regular(file) => file.seek(from),
Self::Block(file) => file.seek(from),
Self::Char(_) => Err(Error::InvalidOperation),
Self::Directory(_) => Err(Error::IsADirectory),
}
}
}
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Regular(file) => f
.debug_struct("RegularFile")
.field("position", &file.position.get())
.field("read", &file.read)
.field("write", &file.write)
.finish_non_exhaustive(),
Self::Block(file) => f
.debug_struct("BlockFile")
.field("position", &file.position.get())
.field("read", &file.read)
.field("write", &file.write)
.finish_non_exhaustive(),
Self::Char(file) => f
.debug_struct("CharFile")
.field("read", &file.read)
.field("write", &file.write)
.finish_non_exhaustive(),
Self::Directory(_) => f.debug_struct("DirectoryFile").finish_non_exhaustive(),
}
}
}
#[cfg(test)]
mod tests {
use core::{any::Any, cell::RefCell, mem::MaybeUninit, str::FromStr};
use std::sync::Arc;
use kernel_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::{
error::Error,
io::{DirectoryEntry, FileType, OpenOptions, SeekFrom},
util::FixedString,
};
use crate::{
device::{BlockDevice, CharDevice},
file::DirectoryOpenPosition,
node::{CommonImpl, DirectoryImpl, Node, NodeRef, RegularImpl},
traits::{Read, Seek, Write},
};
#[test]
fn physical_dir_read() {
struct D {
entries: Vec<(String, NodeRef)>,
}
struct F;
impl CommonImpl for D {}
impl DirectoryImpl for D {
fn open(&self, _node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
Ok(DirectoryOpenPosition::FromPhysical(0))
}
fn read_entries(
&self,
_node: &NodeRef,
pos: u64,
entries: &mut [MaybeUninit<DirectoryEntry>],
) -> Result<(usize, u64), Error> {
let pos = pos as usize;
if pos == self.entries.len() {
return Ok((0, pos as u64));
}
let count = core::cmp::min(entries.len(), self.entries.len() - pos);
for i in 0..count {
let (name, node) = &self.entries[i];
let entry = DirectoryEntry {
name: FixedString::from_str(name)?,
ty: node.ty(),
};
entries[i].write(entry);
}
Ok((count, (pos + count) as u64))
}
}
impl CommonImpl for F {}
impl RegularImpl for F {}
let d = Node::directory(D {
entries: Vec::from_iter([
("f1".to_owned(), Node::regular(F)),
("f2".to_owned(), Node::regular(F)),
("f3".to_owned(), Node::regular(F)),
]),
});
let f = d.open_directory().unwrap();
let mut entries = [MaybeUninit::uninit(); 16];
let count = f.read_dir(&mut entries).unwrap();
assert_eq!(count, 3);
unsafe {
assert_eq!(
MaybeUninit::slice_assume_init_ref(&entries[..count]),
&[
DirectoryEntry {
name: FixedString::from_str("f1").unwrap(),
ty: FileType::File,
},
DirectoryEntry {
name: FixedString::from_str("f2").unwrap(),
ty: FileType::File,
},
DirectoryEntry {
name: FixedString::from_str("f3").unwrap(),
ty: FileType::File
}
]
);
}
let count = f.read_dir(&mut entries).unwrap();
assert_eq!(count, 0);
}
#[test]
fn cache_dir_read() {
struct D;
impl CommonImpl for D {}
impl DirectoryImpl for D {
fn open(&self, _node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
Ok(DirectoryOpenPosition::FromCache)
}
}
let d = Node::directory(D);
let child = Node::directory(D);
d.add_child("child1", child).unwrap();
let f = d.open_directory().unwrap();
let mut entries = [MaybeUninit::uninit(); 16];
let count = f.read_dir(&mut entries).unwrap();
assert_eq!(count, 3);
unsafe {
assert_eq!(
MaybeUninit::slice_assume_init_ref(&entries[..count]),
&[
DirectoryEntry {
name: FixedString::from_str(".").unwrap(),
ty: FileType::Directory
},
DirectoryEntry {
name: FixedString::from_str("..").unwrap(),
ty: FileType::Directory
},
DirectoryEntry {
name: FixedString::from_str("child1").unwrap(),
ty: FileType::Directory
}
]
);
}
let count = f.read_dir(&mut entries).unwrap();
assert_eq!(count, 0);
}
#[test]
fn file_read_write() {
struct F {
data: Arc<RefCell<Vec<u8>>>,
}
impl CommonImpl for F {
fn size(&self, _node: &NodeRef) -> Result<usize, Error> {
Ok(self.data.borrow().len())
}
}
impl RegularImpl for F {
fn open(
&self,
_node: &NodeRef,
_opts: OpenOptions,
) -> Result<(u64, Option<Box<dyn Any>>), Error> {
Ok((0, None))
}
fn read(
&self,
_node: &NodeRef,
_instance: Option<&Box<dyn Any>>,
pos: u64,
buf: &mut [u8],
) -> Result<usize, Error> {
let pos = pos as usize;
let data = self.data.borrow();
if pos >= data.len() {
return Ok(0);
}
let count = core::cmp::min(data.len() - pos, buf.len());
buf[..count].copy_from_slice(&data[pos..pos + count]);
Ok(count)
}
fn write(
&self,
_node: &NodeRef,
_instance: Option<&Box<dyn Any>>,
pos: u64,
buf: &[u8],
) -> Result<usize, Error> {
let pos = pos as usize;
let mut data = self.data.borrow_mut();
data.resize(pos + buf.len(), 0);
data[pos..pos + buf.len()].copy_from_slice(buf);
Ok(buf.len())
}
}
let data = Arc::new(RefCell::new(vec![]));
let node = Node::regular(F { data: data.clone() });
let file = node.open(OpenOptions::READ | OpenOptions::WRITE).unwrap();
let mut buf = [0; 512];
assert_eq!(&*data.borrow(), &[]);
assert_eq!(file.tell().unwrap(), 0);
assert_eq!(file.write(b"Hello").unwrap(), 5);
assert_eq!(file.tell().unwrap(), 5);
assert_eq!(&*data.borrow(), b"Hello");
assert_eq!(file.seek(SeekFrom::End(-2)).unwrap(), 3);
assert_eq!(file.tell().unwrap(), 3);
assert_eq!(file.write(b"123456").unwrap(), 6);
assert_eq!(file.tell().unwrap(), 9);
assert_eq!(&*data.borrow(), b"Hel123456");
assert_eq!(file.seek(SeekFrom::Start(2)).unwrap(), 2);
assert_eq!(file.read(&mut buf).unwrap(), 7);
assert_eq!(file.tell().unwrap(), 9);
assert_eq!(&buf[..7], b"l123456");
}
#[test]
fn block_device() {
struct B {
data: Arc<IrqSafeSpinlock<Vec<u8>>>,
}
impl BlockDevice for B {
fn read(&self, pos: u64, buf: &mut [u8]) -> Result<usize, Error> {
let data = self.data.lock();
let pos = pos as usize;
if pos >= data.len() {
return Ok(0);
}
let count = core::cmp::min(data.len() - pos, buf.len());
buf[..count].copy_from_slice(&data[pos..pos + count]);
Ok(count)
}
fn write(&self, pos: u64, buf: &[u8]) -> Result<usize, Error> {
let mut data = self.data.lock();
let pos = pos as usize;
if pos >= data.len() {
return Ok(0);
}
let count = core::cmp::min(data.len() - pos, buf.len());
data[pos..pos + count].copy_from_slice(&buf[..count]);
Ok(count)
}
fn size(&self) -> Result<usize, Error> {
Ok(self.data.lock().len())
}
}
let vec = vec![0; 1024];
let state = Arc::new(IrqSafeSpinlock::new(vec));
let data = state.clone();
let dev = Box::leak(Box::new(B { data }));
let mut buf = [0; 512];
let node = Node::block(dev);
let file = node.open(OpenOptions::READ | OpenOptions::WRITE).unwrap();
assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 1024);
assert_eq!(file.write(b"12345").unwrap(), 0);
assert_eq!(file.seek(SeekFrom::Start(0)).unwrap(), 0);
assert_eq!(file.write(b"12345").unwrap(), 5);
assert_eq!(&state.lock()[..6], b"12345\0");
assert_eq!(file.read(&mut buf).unwrap(), 512);
assert_eq!(buf, [0; 512]);
assert_eq!(file.seek(SeekFrom::Start(2)).unwrap(), 2);
assert_eq!(file.read(&mut buf[..8]).unwrap(), 8);
assert_eq!(&buf[..8], b"345\0\0\0\0\0");
}
#[test]
fn char_device() {
struct C;
impl CharDevice for C {
fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
buf.fill(b'@');
Ok(buf.len())
}
fn is_writable(&self) -> bool {
false
}
}
static DEV: C = C;
let node = Node::char(&DEV);
let mut buf = [0; 512];
let err = node.open(OpenOptions::WRITE).unwrap_err();
assert_eq!(err, Error::ReadOnly);
let file = node.open(OpenOptions::READ).unwrap();
assert_eq!(file.tell().unwrap_err(), Error::InvalidOperation);
assert_eq!(
file.seek(SeekFrom::Start(10)).unwrap_err(),
Error::InvalidOperation
);
assert_eq!(file.read(&mut buf).unwrap(), 512);
assert_eq!(buf, [b'@'; 512]);
assert_eq!(file.write(b"1234").unwrap_err(), Error::ReadOnly);
}
}