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, pub(crate) inode: InodeAccess, cache: IrqSafeRwLock>, } impl SymlinkNode { pub fn new(fs: Arc, inode: InodeAccess) -> NodeRef { Node::symlink( Self { fs: fs.clone(), inode, cache: IrqSafeRwLock::new(Vec::new()), }, NodeFlags::empty(), None, Some(fs), ) } async fn read(&self, buf: &mut [u8]) -> Result { self.inode .amap(async |inode| { 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.resize(len, 0); block.copy_to(0, &mut write[..]); block.copy_to(0, &mut buf[..len]); Ok(()) }) .await?; } Ok(len) }) .await } unsafe fn link_from_inode_blocks(inode: &Inode, len: usize) -> &[u8] { debug_assert!(len < 60); &bytemuck::bytes_of(&inode.blocks)[..len] } pub(crate) unsafe fn write_link_to_inode_blocks(inode: &mut Inode, link: &[u8]) { debug_assert!(link.len() < 60); bytemuck::bytes_of_mut(&mut inode.blocks)[..link.len()].copy_from_slice(link); inode.size_lower = link.len() as _; } } impl CommonImpl for SymlinkNode { fn size(&self, _node: &NodeRef) -> Result { block!(self.inode.size().await)? } fn as_any(&self) -> &dyn Any { self } fn metadata(&self, _node: &NodeRef) -> Result { block!(self.inode.metadata().await)? } fn set_metadata(&self, _node: &NodeRef, metadata: &Metadata) -> Result<(), Error> { block!(self.inode.update_metadata(metadata).await)? } } impl SymlinkImpl for SymlinkNode { fn read_link(&self, buf: &mut [u8]) -> Result { { 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)? } }