107 lines
2.9 KiB
Rust

use core::any::Any;
use alloc::{sync::Arc, vec::Vec};
use libk::{
block,
error::Error,
vfs::{CommonImpl, Metadata, Node, NodeFlags, NodeRef, SymlinkImpl},
};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use crate::{inode::InodeAccess, Ext2Fs, Inode};
pub struct SymlinkNode {
fs: Arc<Ext2Fs>,
inode: InodeAccess,
cache: IrqSafeRwLock<Vec<u8>>,
}
impl SymlinkNode {
pub fn new(fs: Arc<Ext2Fs>, inode: InodeAccess) -> NodeRef {
Node::symlink(
Self {
fs,
inode,
cache: IrqSafeRwLock::new(Vec::new()),
},
NodeFlags::empty(),
None,
)
}
async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
let inode = self.inode.get().await?;
let inode = inode.read();
let len = inode.size(&self.fs) as usize;
if len > self.fs.block_size {
log::warn!("ext2: symlink size > block size");
return Err(Error::InvalidFile);
}
if buf.len() < len {
return Err(Error::BufferTooSmall);
}
let mut write = self.cache.write();
write.clear();
// If length of symlink is lower than 60, data is stored directly in "block address"
// section of the inode
if len < 60 {
let bytes = unsafe { Self::link_from_inode_blocks(&inode, len) };
write.extend_from_slice(bytes);
buf[..len].copy_from_slice(bytes);
} else {
self.fs
.with_inode_block(&inode, 0, |block| {
write.extend_from_slice(&block[..len]);
buf[..len].copy_from_slice(&block[..len]);
Ok(())
})
.await?;
}
Ok(len)
}
unsafe fn link_from_inode_blocks(inode: &Inode, len: usize) -> &[u8] {
debug_assert!(len < 60);
&bytemuck::bytes_of(&inode.blocks)[..len]
}
}
impl CommonImpl for SymlinkNode {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
let inode = block!(self.inode.get().await)??;
let inode = inode.read();
Ok(inode.size(&self.fs))
}
fn as_any(&self) -> &dyn Any {
self
}
fn metadata(&self, _node: &NodeRef) -> Result<Metadata, Error> {
let inode = block!(self.inode.get().await)??;
let inode = inode.read();
Ok(inode.metadata(&self.fs, self.inode.ino()))
}
}
impl SymlinkImpl for SymlinkNode {
fn read_link(&self, buf: &mut [u8]) -> Result<usize, Error> {
{
let read = self.cache.read();
if buf.len() < read.len() {
todo!();
}
if !read.is_empty() {
buf[..read.len()].copy_from_slice(&read[..]);
return Ok(read.len());
}
}
block!(self.read(buf).await)?
}
}