[libos] Rewrite the getdents

This commit is contained in:
LI Qing 2024-03-26 15:10:54 +08:00 committed by volcano
parent 48b9c077ed
commit d2bed6c862
11 changed files with 214 additions and 164 deletions

2
deps/sefs vendored

@ -1 +1 @@
Subproject commit c2a1fe48b06cf9cedbad7a51d9cb846706630403 Subproject commit 3314355400614c799e237ed1eae5682d27ca1400

@ -67,7 +67,7 @@ pub trait File: Debug + Sync + Send + Any {
return_op_unsupported_error!("set_len") return_op_unsupported_error!("set_len")
} }
fn iterate_entries(&self, writer: &mut dyn DirentWriter) -> Result<usize> { fn iterate_entries(&self, visitor: &mut dyn DirentVisitor) -> Result<usize> {
return_op_unsupported_error!("iterate_entries") return_op_unsupported_error!("iterate_entries")
} }

@ -9,7 +9,7 @@ pub fn do_getdents(fd: FileDesc, buf: &mut [u8]) -> Result<usize> {
getdents_common::<LinuxDirent>(fd, buf) getdents_common::<LinuxDirent>(fd, buf)
} }
fn getdents_common<T: Dirent>(fd: FileDesc, buf: &mut [u8]) -> Result<usize> { fn getdents_common<T: DirentSerializer>(fd: FileDesc, buf: &mut [u8]) -> Result<usize> {
debug!( debug!(
"getdents: fd: {}, buf: {:?}, buf_size: {}", "getdents: fd: {}, buf: {:?}, buf_size: {}",
fd, fd,
@ -23,17 +23,17 @@ fn getdents_common<T: Dirent>(fd: FileDesc, buf: &mut [u8]) -> Result<usize> {
return_errno!(ENOTDIR, ""); return_errno!(ENOTDIR, "");
} }
let mut writer = DirentBufWriter::<T>::new(buf); let mut writer = DirentBufWriter::<T>::new(buf);
let written_len = file_ref.iterate_entries(&mut writer)?; let _ = file_ref.iterate_entries(&mut writer)?;
Ok(written_len) Ok(writer.written_len())
} }
struct DirentBufWriter<'a, T: Dirent> { struct DirentBufWriter<'a, T: DirentSerializer> {
buf: &'a mut [u8], buf: &'a mut [u8],
written_len: usize, written_len: usize,
phantom: PhantomData<T>, phantom: PhantomData<T>,
} }
impl<'a, T: Dirent> DirentBufWriter<'a, T> { impl<'a, T: DirentSerializer> DirentBufWriter<'a, T> {
fn new(buf: &'a mut [u8]) -> Self { fn new(buf: &'a mut [u8]) -> Self {
Self { Self {
buf, buf,
@ -41,29 +41,38 @@ impl<'a, T: Dirent> DirentBufWriter<'a, T> {
phantom: PhantomData, phantom: PhantomData,
} }
} }
}
impl<'a, T: Dirent> DirentWriter for DirentBufWriter<'a, T> { pub fn written_len(&self) -> usize {
fn write_entry(&mut self, name: &str, ino: u64, type_: FileType) -> rcore_fs::vfs::Result<()> {
let dirent: T = Dirent::new(name, ino, type_);
if self.buf.len() - self.written_len < dirent.rec_len() {
return Err(FsError::InvalidParam);
}
dirent
.serialize(&mut self.buf[self.written_len..], name, type_)
.map_err(|_| FsError::InvalidParam)?;
self.written_len += dirent.rec_len();
Ok(())
}
fn written_len(&self) -> usize {
self.written_len self.written_len
} }
} }
trait Dirent: Sync + Send { impl<'a, T: DirentSerializer> DirentVisitor for DirentBufWriter<'a, T> {
fn new(name: &str, ino: u64, type_: FileType) -> Self; fn visit_entry(
&mut self,
name: &str,
ino: u64,
type_: FileType,
offset: usize,
) -> rcore_fs::vfs::Result<()> {
let dirent_serializer = T::new(name, ino, type_, offset);
if self.written_len >= self.buf.len() {
return Err(FsError::InvalidParam);
}
dirent_serializer
.serialize(&mut self.buf[self.written_len..], name, type_)
.map_err(|_| FsError::InvalidParam)?;
self.written_len += dirent_serializer.rec_len();
Ok(())
}
}
trait DirentSerializer: Sync + Send {
/// Creates a new DirentSerializer.
fn new(name: &str, ino: u64, type_: FileType, offset: usize) -> Self;
/// Returns the length.
fn rec_len(&self) -> usize; fn rec_len(&self) -> usize;
/// Tries to serialize a directory entry into buffer.
fn serialize(&self, buf: &mut [u8], name: &str, type_: FileType) -> Result<()>; fn serialize(&self, buf: &mut [u8], name: &str, type_: FileType) -> Result<()>;
} }
@ -83,13 +92,13 @@ struct LinuxDirent64 {
pub name: [u8; 0], pub name: [u8; 0],
} }
impl Dirent for LinuxDirent64 { impl DirentSerializer for LinuxDirent64 {
fn new(name: &str, ino: u64, type_: FileType) -> Self { fn new(name: &str, ino: u64, type_: FileType, offset: usize) -> Self {
let ori_len = core::mem::size_of::<Self>() + name.len() + 1; let ori_len = core::mem::size_of::<Self>() + name.len() + 1;
let len = align_up(ori_len, 8); // align up to 8 bytes let len = align_up(ori_len, 8); // align up to 8 bytes
Self { Self {
ino, ino,
offset: 0, offset: offset as u64,
rec_len: len as u16, rec_len: len as u16,
type_: DirentType::from_file_type(type_), type_: DirentType::from_file_type(type_),
name: [], name: [],
@ -101,6 +110,10 @@ impl Dirent for LinuxDirent64 {
} }
fn serialize(&self, buf: &mut [u8], name: &str, _type_: FileType) -> Result<()> { fn serialize(&self, buf: &mut [u8], name: &str, _type_: FileType) -> Result<()> {
if self.rec_len() > buf.len() {
return_errno!(EINVAL, "not enough buf");
}
unsafe { unsafe {
let ptr = buf.as_mut_ptr() as *mut Self; let ptr = buf.as_mut_ptr() as *mut Self;
ptr.write(*self); ptr.write(*self);
@ -131,14 +144,14 @@ struct LinuxDirent {
*/ */
} }
impl Dirent for LinuxDirent { impl DirentSerializer for LinuxDirent {
fn new(name: &str, ino: u64, type_: FileType) -> Self { fn new(name: &str, ino: u64, type_: FileType, offset: usize) -> Self {
let ori_len = let ori_len =
core::mem::size_of::<Self>() + name.len() + 1 + core::mem::size_of::<FileType>(); core::mem::size_of::<Self>() + name.len() + 1 + core::mem::size_of::<FileType>();
let len = align_up(ori_len, 8); // align up to 8 bytes let len = align_up(ori_len, 8); // align up to 8 bytes
Self { Self {
ino, ino,
offset: 0, offset: offset as u64,
rec_len: len as u16, rec_len: len as u16,
name: [], name: [],
} }
@ -149,6 +162,10 @@ impl Dirent for LinuxDirent {
} }
fn serialize(&self, buf: &mut [u8], name: &str, type_: FileType) -> Result<()> { fn serialize(&self, buf: &mut [u8], name: &str, type_: FileType) -> Result<()> {
if self.rec_len() > buf.len() {
return_errno!(EINVAL, "not enough buf");
}
unsafe { unsafe {
let ptr = buf.as_mut_ptr() as *mut Self; let ptr = buf.as_mut_ptr() as *mut Self;
ptr.write(*self); ptr.write(*self);

@ -250,32 +250,41 @@ impl INode for HNode {
} }
} }
fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> Result<usize> { fn iterate_entries(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
if !self.is_dir() { if !self.is_dir() {
return Err(FsError::NotDir); return Err(FsError::NotDir);
} }
let idx = ctx.pos();
for entry in try_std!(self.path.read_dir()).skip(idx) { let try_iterate = |offset: &mut usize, visitor: &mut dyn DirentVisitor| -> Result<()> {
let start_offset = *offset;
for (idx, entry) in try_std!(self.path.read_dir())
.enumerate()
.skip_while(|(idx, _)| idx < &start_offset)
{
let entry = try_std!(entry); let entry = try_std!(entry);
if let Err(e) = ctx.write_entry( visitor.visit_entry(
&entry &entry
.file_name() .file_name()
.into_string() .into_string()
.map_err(|_| FsError::InvalidParam)?, .map_err(|_| FsError::InvalidParam)?,
entry.ino(), entry.ino() as u64,
entry entry
.file_type() .file_type()
.map_err(|_| FsError::InvalidParam)? .map_err(|_| FsError::InvalidParam)?
.into_fs_filetype(), .into_fs_filetype(),
) { idx,
if ctx.written_len() == 0 { )?;
return Err(e); *offset = idx + 1;
} else {
break;
} }
Ok(())
}; };
let mut iterate_offset = offset;
match try_iterate(&mut iterate_offset, visitor) {
Err(e) if iterate_offset == offset => Err(e),
_ => Ok(iterate_offset - offset),
} }
Ok(ctx.written_len())
} }
fn io_control(&self, cmd: u32, data: usize) -> Result<()> { fn io_control(&self, cmd: u32, data: usize) -> Result<()> {

@ -193,15 +193,14 @@ impl File for INodeFile {
Ok(()) Ok(())
} }
fn iterate_entries(&self, writer: &mut dyn DirentWriter) -> Result<usize> { fn iterate_entries(&self, visitor: &mut dyn DirentVisitor) -> Result<usize> {
if !self.access_mode.readable() { if !self.access_mode.readable() {
return_errno!(EBADF, "File not readable. Can't read entry."); return_errno!(EBADF, "File not readable. Can't read entry.");
} }
let mut offset = self.offset.lock().unwrap(); let mut offset = self.offset.lock().unwrap();
let mut dir_ctx = DirentWriterContext::new(*offset, writer); let visited_len = self.inode.iterate_entries(*offset, visitor)?;
let written_size = self.inode.iterate_entries(&mut dir_ctx)?; *offset += visited_len;
*offset = dir_ctx.pos(); Ok(visited_len)
Ok(written_size)
} }
fn access_mode(&self) -> Result<AccessMode> { fn access_mode(&self) -> Result<AccessMode> {

@ -2,8 +2,7 @@ use super::*;
use process; use process;
use rcore_fs::vfs::{ use rcore_fs::vfs::{
DirentWriter, DirentWriterContext, FileSystem, FileType, FsError, INode, Metadata, Timespec, DirentVisitor, FileSystem, FileType, FsError, INode, Metadata, Timespec, PATH_MAX,
PATH_MAX,
}; };
use std; use std;
use std::any::Any; use std::any::Any;

@ -157,44 +157,51 @@ impl DirProcINode for LockedProcRootINode {
} }
} }
fn iterate_entries(&self, mut ctx: &mut DirentWriterContext) -> vfs::Result<usize> { fn iterate_entries(
&self,
offset: usize,
visitor: &mut dyn DirentVisitor,
) -> vfs::Result<usize> {
let file = self.0.read().unwrap(); let file = self.0.read().unwrap();
let idx = ctx.pos();
// Write first two special entries let try_iterate =
if idx == 0 { |mut offset: &mut usize, mut visitor: &mut dyn DirentVisitor| -> vfs::Result<()> {
// The two special entries
if *offset == 0 {
let this_inode = file.this.upgrade().unwrap(); let this_inode = file.this.upgrade().unwrap();
write_inode_entry!(&mut ctx, ".", &this_inode); rcore_fs::visit_inode_entry!(&mut visitor, ".", &this_inode, &mut offset);
} }
if idx <= 1 { if *offset == 1 {
let parent_inode = file.this.upgrade().unwrap(); let parent_inode = file.this.upgrade().unwrap();
write_inode_entry!(&mut ctx, "..", &parent_inode); rcore_fs::visit_inode_entry!(&mut visitor, "..", &parent_inode, &mut offset);
} }
// Write the non volatile entries // The non volatile entries
let skipped = if idx < 2 { 0 } else { idx - 2 }; let start_offset = *offset;
for (name, inode) in file.non_volatile_entries.iter().skip(skipped) { for (name, child) in file.non_volatile_entries.iter().skip(start_offset - 2) {
write_inode_entry!(&mut ctx, name, inode); rcore_fs::visit_inode_entry!(&mut visitor, name, child, &mut offset);
} }
// Write the pid entries // The pid entries
let skipped = { let start_offset = *offset;
let prior_entries_len = 2 + file.non_volatile_entries.len(); let skipped_len = 2 + file.non_volatile_entries.len();
if idx < prior_entries_len { for process in get_all_processes().iter().skip(start_offset - skipped_len) {
0 rcore_fs::visit_entry!(
} else { &mut visitor,
idx - prior_entries_len
}
};
for process in get_all_processes().iter().skip(skipped) {
write_entry!(
&mut ctx,
&process.pid().to_string(), &process.pid().to_string(),
PROC_INO, PROC_INO,
vfs::FileType::Dir vfs::FileType::Dir,
&mut offset
); );
} }
Ok(ctx.written_len()) Ok(())
};
let mut iterate_offset = offset;
match try_iterate(&mut iterate_offset, visitor) {
Err(e) if iterate_offset == offset => Err(e),
_ => Ok(iterate_offset - offset),
}
} }
} }

@ -53,26 +53,45 @@ impl DirProcINode for LockedProcFdDirINode {
} }
} }
fn iterate_entries(&self, mut ctx: &mut DirentWriterContext) -> vfs::Result<usize> { fn iterate_entries(
&self,
offset: usize,
visitor: &mut dyn DirentVisitor,
) -> vfs::Result<usize> {
let file = self.0.read().unwrap(); let file = self.0.read().unwrap();
let idx = ctx.pos();
// Write first two special entries let try_iterate =
write_first_two_entries!(idx, &mut ctx, &file); |mut offset: &mut usize, mut visitor: &mut dyn DirentVisitor| -> vfs::Result<()> {
// The two special entries
visit_first_two_entries!(&mut visitor, &file, &mut offset);
// Write the fd entries // The fd entries
let skipped = if idx < 2 { 0 } else { idx - 2 };
let main_thread = match file.process_ref.main_thread() { let main_thread = match file.process_ref.main_thread() {
Some(main_thread) => main_thread, Some(main_thread) => main_thread,
None => { None => {
return Ok(ctx.written_len()); return Ok(());
} }
}; };
let fds = main_thread.files().lock().unwrap().fds(); let fds = main_thread.files().lock().unwrap().fds();
for fd in fds.iter().skip(skipped) { let start_offset = *offset;
write_entry!(&mut ctx, &fd.to_string(), PROC_INO, vfs::FileType::SymLink); for fd in fds.iter().skip(start_offset - 2) {
rcore_fs::visit_entry!(
&mut visitor,
&fd.to_string(),
PROC_INO,
vfs::FileType::SymLink,
&mut offset
);
}
Ok(())
};
let mut iterate_offset = offset;
match try_iterate(&mut iterate_offset, visitor) {
Err(e) if iterate_offset == offset => Err(e),
_ => Ok(iterate_offset - offset),
} }
Ok(ctx.written_len())
} }
} }

@ -113,23 +113,42 @@ impl DirProcINode for LockedPidDirINode {
} }
} }
fn iterate_entries(&self, mut ctx: &mut DirentWriterContext) -> vfs::Result<usize> { fn iterate_entries(
&self,
offset: usize,
visitor: &mut dyn DirentVisitor,
) -> vfs::Result<usize> {
let file = self.0.read().unwrap(); let file = self.0.read().unwrap();
let idx = ctx.pos();
// Write first two special entries let try_iterate =
write_first_two_entries!(idx, &mut ctx, &file); |mut offset: &mut usize, mut visitor: &mut dyn DirentVisitor| -> vfs::Result<()> {
// The two special entries
visit_first_two_entries!(&mut visitor, &file, &mut offset);
// Write the normal entries // The normal entries
let skipped = if idx < 2 { 0 } else { idx - 2 }; let start_offset = *offset;
for (name, inode) in file.entries.iter().skip(skipped) { for (name, child) in file.entries.iter().skip(start_offset - 2) {
write_inode_entry!(&mut ctx, name, inode); rcore_fs::visit_inode_entry!(&mut visitor, name, child, &mut offset);
} }
// Write the fd entry // The fd entry
if idx <= 2 + file.entries.len() { if *offset == 2 + file.entries.len() {
write_entry!(&mut ctx, "fd", PROC_INO, vfs::FileType::Dir); rcore_fs::visit_entry!(
&mut visitor,
"fd",
PROC_INO as u64,
vfs::FileType::Dir,
&mut offset
);
}
Ok(())
};
let mut iterate_offset = offset;
match try_iterate(&mut iterate_offset, visitor) {
Err(e) if iterate_offset == offset => Err(e),
_ => Ok(iterate_offset - offset),
} }
Ok(ctx.written_len())
} }
} }

@ -65,8 +65,12 @@ where
self.inner().get_entry(id) self.inner().get_entry(id)
} }
fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> vfs::Result<usize> { fn iterate_entries(
self.inner().iterate_entries(ctx) &self,
offset: usize,
visitor: &mut dyn DirentVisitor,
) -> vfs::Result<usize> {
self.inner().iterate_entries(offset, visitor)
} }
fn as_any_ref(&self) -> &dyn Any { fn as_any_ref(&self) -> &dyn Any {

@ -15,7 +15,8 @@ pub trait ProcINode {
pub trait DirProcINode { pub trait DirProcINode {
fn find(&self, name: &str) -> vfs::Result<Arc<dyn INode>>; fn find(&self, name: &str) -> vfs::Result<Arc<dyn INode>>;
fn get_entry(&self, id: usize) -> vfs::Result<String>; fn get_entry(&self, id: usize) -> vfs::Result<String>;
fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> vfs::Result<usize>; fn iterate_entries(&self, offset: usize, visitor: &mut dyn DirentVisitor)
-> vfs::Result<usize>;
} }
#[macro_export] #[macro_export]
@ -54,7 +55,11 @@ macro_rules! impl_inode_for_file_or_symlink {
Err(FsError::NotDir) Err(FsError::NotDir)
} }
fn iterate_entries(&self, ctx: &mut DirentWriterContext) -> vfs::Result<usize> { fn iterate_entries(
&self,
offset: usize,
visitor: &mut dyn DirentVisitor,
) -> vfs::Result<usize> {
Err(FsError::NotDir) Err(FsError::NotDir)
} }
@ -65,47 +70,19 @@ macro_rules! impl_inode_for_file_or_symlink {
} }
#[macro_export] #[macro_export]
macro_rules! write_first_two_entries { macro_rules! visit_first_two_entries {
($idx: expr, $ctx:expr, $file:expr) => { ($visitor:expr, $file:expr, $offset: expr) => {
let idx = $idx; use rcore_fs::visit_inode_entry;
let file = $file; let file = $file;
if idx == 0 { let offset = **$offset;
if offset == 0 {
let this_inode = file.this.upgrade().unwrap(); let this_inode = file.this.upgrade().unwrap();
write_inode_entry!($ctx, ".", &this_inode); visit_inode_entry!($visitor, ".", &this_inode, $offset);
}
if idx <= 1 {
write_inode_entry!($ctx, "..", &file.parent);
}
};
}
#[macro_export]
macro_rules! write_inode_entry {
($ctx:expr, $name:expr, $inode:expr) => {
let ctx = $ctx;
let name = $name;
let ino = $inode.metadata()?.inode;
let type_ = $inode.metadata()?.type_;
write_entry!(ctx, name, ino, type_);
};
}
#[macro_export]
macro_rules! write_entry {
($ctx:expr, $name:expr, $ino:expr, $type_:expr) => {
let ctx = $ctx;
let name = $name;
let ino = $ino;
let type_ = $type_;
if let Err(e) = ctx.write_entry(name, ino as u64, type_) {
if ctx.written_len() == 0 {
return Err(e);
} else {
return Ok(ctx.written_len());
} }
let offset = **$offset;
if offset == 1 {
visit_inode_entry!($visitor, "..", &file.parent, $offset);
} }
}; };
} }