yggdrasil/lib/vfs/src/node/impls.rs

482 lines
12 KiB
Rust

//! Various helper node implementations for convenience
use core::{marker::PhantomData, str::FromStr};
use alloc::{
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use kernel_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::{error::Error, io::OpenOptions};
use crate::{DirectoryOpenPosition, InstanceData};
use super::{CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl, SymlinkImpl};
trait SliceRead {
fn read_slice(&self, offset: usize, buf: &mut [u8]) -> usize;
}
trait SliceWrite {
fn write_slice(&mut self, offset: usize, buf: &[u8]) -> usize;
}
trait AsInstanceData {
fn as_instance_data(&self) -> Vec<u8>;
}
/// Closure interface for reading a single value
pub trait ValueReadFn<T> = Fn() -> Result<T, Error> + Send + Sync;
/// Closure interface for writing a single value
pub trait ValueWriteFn<T> = Fn(T) -> Result<(), Error> + Send + Sync;
/// Closure interface for reading bytes
pub trait ReadFn = Fn(u64, &mut [u8]) -> Result<usize, Error> + Send + Sync;
/// Closure interface for writing bytes
pub trait WriteFn = Fn(u64, &[u8]) -> Result<usize, Error> + Send + Sync;
impl<T: AsRef<[u8]>> SliceRead for T {
fn read_slice(&self, pos: usize, buf: &mut [u8]) -> usize {
let value = self.as_ref();
if pos >= value.len() {
return 0;
}
let count = core::cmp::min(value.len() - pos, buf.len());
buf[..count].copy_from_slice(&value[pos..pos + count]);
count
}
}
impl SliceWrite for Vec<u8> {
fn write_slice(&mut self, offset: usize, buf: &[u8]) -> usize {
if offset + buf.len() > self.len() {
self.resize(offset + buf.len(), 0);
}
self[offset..offset + buf.len()].copy_from_slice(buf);
buf.len()
}
}
impl<T: ToString> AsInstanceData for T {
fn as_instance_data(&self) -> Vec<u8> {
self.to_string().as_bytes().to_vec()
}
}
enum FnNodeData {
Read(Vec<u8>),
Write(IrqSafeSpinlock<Vec<u8>>),
}
/// Allows read-only access to a value. The value is converted to a string representation when it's
/// read.
pub struct ReadOnlyFnValueNode<T: ToString, R: ValueReadFn<T>> {
read: R,
_pd: PhantomData<T>,
}
/// Allows read-write access to a value (but not both at the same time). The value is converted
/// to/from a string representation when it's read/written.
pub struct FnValueNode<T: ToString + FromStr, R: ValueReadFn<T>, W: ValueWriteFn<T>> {
read: R,
write: W,
_pd: PhantomData<T>,
}
/// Allows read-only access to a "functional file"
pub struct ReadOnlyFnNode<R: ReadFn> {
read: R,
}
/// In-memory directory using tree cache
pub struct MemoryDirectory;
/// In-memory symlink pointing to a fixed [Node]
pub struct FixedSymlink {
target: NodeRef,
}
impl<T, R> ReadOnlyFnValueNode<T, R>
where
T: ToString + Send + Sync + 'static,
R: ValueReadFn<T> + 'static,
{
/// Creates a new [ReadOnlyFnValueNode] with given read function
pub fn new(read: R) -> NodeRef {
Node::regular(
Self::new_impl(read),
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
)
}
const fn new_impl(read: R) -> Self {
Self {
read,
_pd: PhantomData,
}
}
}
impl<T, R> CommonImpl for ReadOnlyFnValueNode<T, R>
where
T: ToString + Send + Sync,
R: ValueReadFn<T>,
{
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(0)
}
}
impl<T, R> RegularImpl for ReadOnlyFnValueNode<T, R>
where
T: ToString + Send + Sync,
R: ValueReadFn<T>,
{
fn open(
&self,
_node: &NodeRef,
opts: OpenOptions,
) -> Result<(u64, Option<InstanceData>), Error> {
if opts.contains(OpenOptions::WRITE) {
return Err(Error::ReadOnly);
}
let t = (self.read)()?;
Ok((0, Some(Arc::new(t.as_instance_data()))))
}
fn read(
&self,
_node: &NodeRef,
instance: Option<&InstanceData>,
pos: u64,
buf: &mut [u8],
) -> Result<usize, Error> {
let value = instance.unwrap().downcast_ref::<Vec<u8>>().unwrap();
Ok(value.read_slice(pos as usize, buf))
}
fn write(
&self,
_node: &NodeRef,
_instance: Option<&InstanceData>,
_pos: u64,
_buf: &[u8],
) -> Result<usize, Error> {
Err(Error::ReadOnly)
}
fn truncate(&self, _node: &NodeRef, _new_size: u64) -> Result<(), Error> {
Err(Error::ReadOnly)
}
}
// Read-write FnNode
impl FnNodeData {
fn write() -> Self {
Self::Write(IrqSafeSpinlock::new(Vec::new()))
}
fn read<T: AsInstanceData>(value: T) -> Self {
Self::Read(value.as_instance_data())
}
fn as_read(&self) -> Result<&Vec<u8>, Error> {
match self {
Self::Read(r) => Ok(r),
Self::Write(_) => Err(Error::InvalidOperation),
}
}
fn as_write(&self) -> Result<&IrqSafeSpinlock<Vec<u8>>, Error> {
match self {
Self::Write(w) => Ok(w),
Self::Read(_) => Err(Error::InvalidOperation),
}
}
}
impl<T, R, W> FnValueNode<T, R, W>
where
T: ToString + FromStr + Send + Sync + 'static,
R: ValueReadFn<T> + 'static,
W: ValueWriteFn<T> + 'static,
{
/// Creates a new [FnValueNode] with given read and write functions
pub fn new(read: R, write: W) -> NodeRef {
Node::regular(
Self::new_impl(read, write),
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
)
}
const fn new_impl(read: R, write: W) -> Self {
Self {
read,
write,
_pd: PhantomData,
}
}
}
impl<T, R, W> CommonImpl for FnValueNode<T, R, W>
where
T: ToString + FromStr + Send + Sync,
R: ValueReadFn<T>,
W: ValueWriteFn<T>,
{
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(0)
}
}
impl<T, R, W> RegularImpl for FnValueNode<T, R, W>
where
T: ToString + FromStr + Send + Sync,
R: ValueReadFn<T>,
W: ValueWriteFn<T>,
{
fn open(
&self,
_node: &NodeRef,
opts: OpenOptions,
) -> Result<(u64, Option<InstanceData>), Error> {
if opts.contains(OpenOptions::READ | OpenOptions::WRITE) {
Err(Error::InvalidOperation)
} else if opts.contains(OpenOptions::WRITE) {
Ok((0, Some(Arc::new(FnNodeData::write()))))
} else if opts.contains(OpenOptions::READ) {
let t = (self.read)()?;
Ok((0, Some(Arc::new(FnNodeData::read(t)))))
} else {
Err(Error::InvalidOperation)
}
}
fn close(&self, _node: &NodeRef, instance: Option<&InstanceData>) -> Result<(), Error> {
if let Ok(write) = instance
.unwrap()
.downcast_ref::<FnNodeData>()
.unwrap()
.as_write()
{
let write = write.lock();
// Flush write
let str = core::str::from_utf8(write.as_ref())
.map_err(|_| Error::InvalidArgument)?
.trim();
let t = T::from_str(str).map_err(|_| Error::InvalidArgument)?;
(self.write)(t)?;
}
Ok(())
}
fn read(
&self,
_node: &NodeRef,
instance: Option<&InstanceData>,
pos: u64,
buf: &mut [u8],
) -> Result<usize, Error> {
let instance = instance.unwrap().downcast_ref::<FnNodeData>().unwrap();
Ok(instance.as_read()?.read_slice(pos as usize, buf))
}
fn write(
&self,
_node: &NodeRef,
instance: Option<&InstanceData>,
pos: u64,
buf: &[u8],
) -> Result<usize, Error> {
let instance = instance.unwrap().downcast_ref::<FnNodeData>().unwrap();
Ok(instance.as_write()?.lock().write_slice(pos as _, buf))
}
fn truncate(&self, _node: &NodeRef, _new_size: u64) -> Result<(), Error> {
Ok(())
}
}
// Byte read-only node
impl<R> ReadOnlyFnNode<R>
where
R: ReadFn + 'static,
{
/// Creates a new [ReadOnlyFnNode] with given read function
pub fn new(read: R) -> NodeRef {
Node::regular(Self { read }, NodeFlags::IN_MEMORY_PROPS)
}
}
impl<R> CommonImpl for ReadOnlyFnNode<R>
where
R: ReadFn,
{
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(0)
}
}
impl<R> RegularImpl for ReadOnlyFnNode<R>
where
R: ReadFn,
{
fn open(
&self,
_node: &NodeRef,
opts: OpenOptions,
) -> Result<(u64, Option<InstanceData>), Error> {
if opts.contains(OpenOptions::WRITE) {
return Err(Error::ReadOnly);
}
Ok((0, None))
}
fn read(
&self,
_node: &NodeRef,
_instance: Option<&InstanceData>,
pos: u64,
buf: &mut [u8],
) -> Result<usize, Error> {
(self.read)(pos, buf)
}
}
// In-memory directory
impl MemoryDirectory {
/// Creates a [MemoryDirectory] with no children
pub fn empty() -> NodeRef {
Node::directory(
MemoryDirectory,
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
)
}
}
impl CommonImpl for MemoryDirectory {}
impl DirectoryImpl for MemoryDirectory {
fn open(&self, _node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
Ok(DirectoryOpenPosition::FromCache)
}
}
// In-memory fixed symlink
impl CommonImpl for FixedSymlink {}
impl SymlinkImpl for FixedSymlink {
fn target(&self, _node: &NodeRef) -> Result<NodeRef, Error> {
Ok(self.target.clone())
}
}
/// Creates a read-only value node with given `value`
pub fn const_value_node<T>(value: T) -> NodeRef
where
T: ToString + Clone + Send + Sync + 'static,
{
ReadOnlyFnValueNode::new(move || Ok(value.clone()))
}
/// Creates a read-write value node with given `value`
pub fn value_node<T>(value: T) -> NodeRef
where
T: ToString + FromStr + Clone + Send + Sync + 'static,
{
let rd_state = Arc::new(IrqSafeSpinlock::new(value));
let wr_state = rd_state.clone();
FnValueNode::new(
move || Ok(rd_state.lock().clone()),
move |t| {
*wr_state.lock() = t;
Ok(())
},
)
}
/// Creates a read-only node with given read function
pub fn read_fn_node<R: ReadFn + 'static>(read: R) -> NodeRef {
ReadOnlyFnNode::new(read)
}
/// Creates an in-memory directory from the iterator
pub fn mdir<S: Into<String>, I: IntoIterator<Item = (S, NodeRef)>>(it: I) -> NodeRef {
let dir = Node::directory(
MemoryDirectory,
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
);
for (name, node) in it {
dir.add_child(name, node).unwrap();
}
dir
}
/// Creates a static symlink pointing to given node
pub fn f_symlink(target: NodeRef) -> NodeRef {
Node::symlink(FixedSymlink { target }, NodeFlags::IN_MEMORY_PROPS)
}
#[cfg(test)]
mod tests {
use yggdrasil_abi::io::OpenOptions;
use crate::{
node::{
impls::{const_value_node, value_node},
AccessToken,
},
traits::{Read, Seek, Write},
};
#[test]
fn read_only_fn_node() {
let node = const_value_node("abcdef");
let file = node
.open(OpenOptions::READ, AccessToken::test_authorized())
.unwrap();
let mut buf = [0; 512];
assert_eq!(file.tell().unwrap(), 0);
assert_eq!(file.read(&mut buf[..3]).unwrap(), 3);
assert_eq!(&buf[..3], b"abc");
assert_eq!(file.read(&mut buf).unwrap(), 3);
assert_eq!(&buf[..3], b"def");
}
#[test]
fn fn_node() {
let node = value_node(1234);
let mut buf = [0; 512];
// Try read the value
let file = node
.open(OpenOptions::READ, AccessToken::test_authorized())
.unwrap();
assert_eq!(file.tell().unwrap(), 0);
assert_eq!(file.read(&mut buf).unwrap(), 4);
assert_eq!(&buf[..4], b"1234");
// Try write the value
let file = node
.open(OpenOptions::WRITE, AccessToken::test_authorized())
.unwrap();
assert_eq!(file.tell().unwrap(), 0);
assert_eq!(file.write(b"654321").unwrap(), 6);
drop(file);
// Try read the value again
let file = node
.open(OpenOptions::READ, AccessToken::test_authorized())
.unwrap();
assert_eq!(file.tell().unwrap(), 0);
assert_eq!(file.read(&mut buf).unwrap(), 6);
assert_eq!(&buf[..6], b"654321");
}
}