fs/ext2: add symlinks
This commit is contained in:
parent
dfd45f0ab9
commit
128b699352
@ -103,10 +103,7 @@ pub struct Inode {
|
||||
pub sector_count: u32,
|
||||
pub flags: u32,
|
||||
pub os_val1: u32,
|
||||
pub direct_blocks: [u32; DIRECT_BLOCK_COUNT],
|
||||
pub indirect_block_l1: u32,
|
||||
pub indirect_block_l2: u32,
|
||||
pub indirect_block_l3: u32,
|
||||
pub blocks: InodeBlockRefs,
|
||||
pub generation: u32,
|
||||
pub facl: u32,
|
||||
pub size_upper: u32,
|
||||
@ -114,6 +111,15 @@ pub struct Inode {
|
||||
pub os_val2: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct InodeBlockRefs {
|
||||
pub direct_blocks: [u32; DIRECT_BLOCK_COUNT],
|
||||
pub indirect_block_l1: u32,
|
||||
pub indirect_block_l2: u32,
|
||||
pub indirect_block_l3: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Dirent {
|
||||
@ -202,7 +208,8 @@ impl InodeMode {
|
||||
match self.0 & 0xF000 {
|
||||
0x4000 => Some(FileType::Directory),
|
||||
0x8000 => Some(FileType::File),
|
||||
_ => todo!(),
|
||||
0xA000 => Some(FileType::Symlink),
|
||||
val => todo!("Unhandled ext2 node type: {:#x}", val),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,10 +21,12 @@ use libk_util::OneTimeInit;
|
||||
mod data;
|
||||
pub mod dir;
|
||||
pub mod file;
|
||||
pub mod symlink;
|
||||
|
||||
pub use data::{
|
||||
BlockGroupDescriptor, BlockGroupDescriptorTable, Dirent, ExtendedSuperblock, Inode, Superblock,
|
||||
};
|
||||
use symlink::SymlinkNode;
|
||||
use yggdrasil_abi::io::FileType;
|
||||
|
||||
pub struct Ext2Fs {
|
||||
@ -128,6 +130,7 @@ impl Ext2Fs {
|
||||
match inode.mode.node_type() {
|
||||
Some(FileType::Directory) => Ok(DirectoryNode::new(self.clone(), inode, ino)),
|
||||
Some(FileType::File) => Ok(RegularNode::new(self.clone(), inode, ino)),
|
||||
Some(FileType::Symlink) => Ok(SymlinkNode::new(self.clone(), inode, ino)),
|
||||
e => todo!("Unhandled inode type: {:?} ({:#x?})", e, inode.mode),
|
||||
}
|
||||
}
|
||||
@ -180,21 +183,24 @@ impl Ext2Fs {
|
||||
async fn inode_block_index(&self, inode: &Inode, index: u32) -> Result<u32, Error> {
|
||||
let mut index = index as usize;
|
||||
// L0
|
||||
if index < inode.direct_blocks.len() {
|
||||
return Ok(inode.direct_blocks[index]);
|
||||
if index < data::DIRECT_BLOCK_COUNT {
|
||||
return Ok(inode.blocks.direct_blocks[index]);
|
||||
}
|
||||
|
||||
// L1
|
||||
index -= inode.direct_blocks.len();
|
||||
index -= data::DIRECT_BLOCK_COUNT;
|
||||
if index < self.pointers_per_block {
|
||||
return self.read_index(inode.indirect_block_l1, index).await;
|
||||
return self.read_index(inode.blocks.indirect_block_l1, index).await;
|
||||
}
|
||||
|
||||
// L2
|
||||
index -= self.pointers_per_block;
|
||||
if index < self.pointers_per_block * self.pointers_per_block {
|
||||
let l1_index = self
|
||||
.read_index(inode.indirect_block_l2, index / self.pointers_per_block)
|
||||
.read_index(
|
||||
inode.blocks.indirect_block_l2,
|
||||
index / self.pointers_per_block,
|
||||
)
|
||||
.await?;
|
||||
return self
|
||||
.read_index(l1_index, index % self.pointers_per_block)
|
||||
@ -207,7 +213,7 @@ impl Ext2Fs {
|
||||
let l2_pointer_index = index / self.pointers_per_block;
|
||||
let l2_index = self
|
||||
.read_index(
|
||||
inode.indirect_block_l3,
|
||||
inode.blocks.indirect_block_l3,
|
||||
l2_pointer_index / self.pointers_per_block,
|
||||
)
|
||||
.await?;
|
||||
|
95
kernel/driver/fs/ext2/src/symlink.rs
Normal file
95
kernel/driver/fs/ext2/src/symlink.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use core::any::Any;
|
||||
|
||||
use alloc::{string::String, 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::{Ext2Fs, Inode};
|
||||
|
||||
pub struct SymlinkNode {
|
||||
fs: Arc<Ext2Fs>,
|
||||
inode: Inode,
|
||||
#[allow(unused)]
|
||||
ino: u32,
|
||||
|
||||
cache: IrqSafeRwLock<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl SymlinkNode {
|
||||
pub fn new(fs: Arc<Ext2Fs>, inode: Inode, ino: u32) -> NodeRef {
|
||||
Node::symlink(
|
||||
Self {
|
||||
fs,
|
||||
inode,
|
||||
ino,
|
||||
cache: IrqSafeRwLock::new(Vec::new()),
|
||||
},
|
||||
NodeFlags::empty(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
let len = self.inode.size_lower as usize;
|
||||
if len >= self.fs.block_size {
|
||||
todo!()
|
||||
}
|
||||
|
||||
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(len) };
|
||||
write.extend_from_slice(bytes);
|
||||
buf[..len].copy_from_slice(bytes);
|
||||
} else {
|
||||
let block = self.fs.inode_block(&self.inode, 0).await?;
|
||||
write.extend_from_slice(&block[..len]);
|
||||
buf[..len].copy_from_slice(&block[..len]);
|
||||
}
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
unsafe fn link_from_inode_blocks(&self, len: usize) -> &[u8] {
|
||||
debug_assert!(len < 60);
|
||||
&bytemuck::bytes_of(&self.inode.blocks)[..len]
|
||||
}
|
||||
}
|
||||
|
||||
impl CommonImpl for SymlinkNode {
|
||||
fn size(&self, node: &NodeRef) -> Result<u64, Error> {
|
||||
Ok(self.inode.size_lower as _)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn metadata(&self, node: &NodeRef) -> Result<Metadata, Error> {
|
||||
Ok(self.inode.metadata())
|
||||
}
|
||||
}
|
||||
|
||||
impl SymlinkImpl for SymlinkNode {
|
||||
fn read_link(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
if buf.len() < self.inode.size_lower as usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
{
|
||||
let read = self.cache.read();
|
||||
if !read.is_empty() {
|
||||
buf[..read.len()].copy_from_slice(&read[..]);
|
||||
return Ok(read.len());
|
||||
}
|
||||
}
|
||||
|
||||
block!(self.read(buf).await)?
|
||||
}
|
||||
}
|
@ -90,6 +90,14 @@ impl<'a, D: NgBlockDevice + 'a> BlockDevice for Partition<'a, D> {
|
||||
|| buf.len() % self.device.block_size != 0
|
||||
{
|
||||
// TODO fallback to unaligned read
|
||||
log::info!(
|
||||
"Unaligned read: block_size={}, off={:#x}, pos={:#x}, len={}",
|
||||
self.device.block_size,
|
||||
pos,
|
||||
start,
|
||||
buf.len()
|
||||
);
|
||||
loop {}
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
@ -299,7 +299,7 @@ impl IoContext {
|
||||
|
||||
/// Locates a [crate::Node] pointed to by given [Path]
|
||||
pub fn find<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
&self,
|
||||
at: Option<NodeRef>,
|
||||
path: P,
|
||||
follow_links: bool,
|
||||
@ -321,10 +321,15 @@ impl IoContext {
|
||||
fn _resolve_link(&self, at: &NodeRef) -> Result<NodeRef, Error> {
|
||||
let token = self.check_access(Action::Read, at)?;
|
||||
// let _path = link.imp.read_to_string()?;
|
||||
match at.read_symlink_node(token) {
|
||||
match at.read_symlink_node(token.clone()) {
|
||||
Ok(node) => Ok(node),
|
||||
// Need to read the link data and resolve it manually
|
||||
Err(Error::NotImplemented) => todo!(),
|
||||
Err(Error::NotImplemented) => {
|
||||
let path = at.read_symlink_path(token)?;
|
||||
let target = self._find(at.parent(), path.as_ref(), true, true)?;
|
||||
// TODO update node cache
|
||||
Ok(target)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
@ -327,6 +327,13 @@ impl Node {
|
||||
Ok(cache.insert((String::new(), target)).1.clone())
|
||||
}
|
||||
|
||||
pub(crate) fn read_symlink_path(self: &NodeRef, _token: AccessToken) -> Result<String, Error> {
|
||||
let symlink = self.as_symlink()?;
|
||||
|
||||
// TODO cache path
|
||||
symlink.imp.read_to_string()
|
||||
}
|
||||
|
||||
pub fn as_block_device(&self) -> Result<&'static dyn BlockDevice, Error> {
|
||||
match &self.data {
|
||||
NodeImpl::Block(dev) => Ok(dev.0),
|
||||
|
@ -70,17 +70,23 @@ impl Node {
|
||||
_check: AccessToken,
|
||||
) -> Result<NodeRef, Error> {
|
||||
let dir = self.as_directory()?;
|
||||
let children = dir.children.lock();
|
||||
|
||||
if let Some((_, node)) = children.iter().find(|(name_, _)| name_ == name) {
|
||||
return Ok(node.clone());
|
||||
{
|
||||
let children = dir.children.lock();
|
||||
|
||||
if let Some((_, node)) = children.iter().find(|(name_, _)| name_ == name) {
|
||||
return Ok(node.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO lookup in real FS
|
||||
match dir.imp.lookup(self, name) {
|
||||
let node = match dir.imp.lookup(self, name) {
|
||||
Err(Error::NotImplemented) => Err(Error::DoesNotExist),
|
||||
res => res,
|
||||
}
|
||||
}?;
|
||||
|
||||
self.add_child(name, node.clone())?;
|
||||
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
/// Creates an entry within a directory with given [CreateInfo].
|
||||
@ -215,4 +221,9 @@ impl Node {
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_link(self: &NodeRef, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let symlink = self.as_symlink()?;
|
||||
symlink.imp.read_link(buffer)
|
||||
}
|
||||
}
|
||||
|
@ -192,6 +192,21 @@ pub(crate) fn get_metadata(
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn read_link(at: Option<RawFd>, path: &str, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let thread = Thread::current();
|
||||
let process = thread.process();
|
||||
|
||||
run_with_io_at(&process, at, |at, mut io| {
|
||||
let node = if path.is_empty() {
|
||||
at
|
||||
} else {
|
||||
io.ioctx_mut().find(Some(at), path, false, true)?
|
||||
};
|
||||
|
||||
node.read_link(buffer)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn device_request(fd: RawFd, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
let thread = Thread::current();
|
||||
let process = thread.process();
|
||||
|
@ -90,6 +90,7 @@ syscall read_directory_entries(fd: RawFd, entries: &mut [MaybeUninit<DirectoryEn
|
||||
syscall create_directory(at: Option<RawFd>, path: &str, mode: FileMode) -> Result<()>;
|
||||
syscall remove_directory(at: Option<RawFd>, path: &str) -> Result<()>;
|
||||
|
||||
syscall read_link(at: Option<RawFd>, path: &str, buf: &mut [u8]) -> Result<usize>;
|
||||
syscall remove(at: Option<RawFd>, path: &str) -> Result<()>;
|
||||
syscall clone_fd(source: RawFd, target: Option<RawFd>) -> Result<RawFd>;
|
||||
syscall update_metadata(at: Option<RawFd>, path: &str, update: &FileMetadataUpdate) -> Result<()>;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#![cfg_attr(target_os = "yggdrasil", feature(yggdrasil_os))]
|
||||
#![feature(let_chains)]
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
@ -79,6 +80,7 @@ impl<T: DisplaySizeBit + Copy> fmt::Display for DisplaySizeWith<'_, T> {
|
||||
|
||||
struct Entry {
|
||||
name: String,
|
||||
target: Option<String>,
|
||||
ty: Option<FileType>,
|
||||
attrs: Option<Metadata>,
|
||||
}
|
||||
@ -134,7 +136,13 @@ impl DisplayBit for Entry {
|
||||
self.ty.display_with(opts),
|
||||
self.attrs.display_with(opts),
|
||||
self.name
|
||||
)
|
||||
)?;
|
||||
|
||||
if let Some(target) = self.target.as_ref() {
|
||||
write!(f, " -> {}", target)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
f.write_str(&self.name)
|
||||
}
|
||||
@ -145,6 +153,7 @@ impl Entry {
|
||||
fn invalid() -> Self {
|
||||
Self {
|
||||
name: "???".to_owned(),
|
||||
target: None,
|
||||
ty: None,
|
||||
attrs: None,
|
||||
}
|
||||
@ -161,10 +170,22 @@ fn list_directory(path: &Path) -> io::Result<Vec<Entry>> {
|
||||
|
||||
let os_filename = entry.file_name();
|
||||
let ty = entry.file_type().ok();
|
||||
let attrs = entry.path().metadata().ok();
|
||||
let attrs = entry.path().symlink_metadata().ok();
|
||||
|
||||
let target = if let Some(attrs) = attrs.as_ref()
|
||||
&& attrs.is_symlink()
|
||||
{
|
||||
Some(match entry.path().read_link() {
|
||||
Ok(res) => res.to_string_lossy().to_string(),
|
||||
Err(_) => "???".into(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
entries.push(Entry {
|
||||
name: os_filename.to_string_lossy().to_string(),
|
||||
target,
|
||||
ty,
|
||||
attrs,
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user