Add syscall getdents

This commit is contained in:
Fabing Li
2024-06-06 10:46:04 +08:00
committed by Tate, Hongliang Tian
parent 4700ab71bc
commit 053f8d416e
4 changed files with 95 additions and 19 deletions

View File

@ -98,7 +98,7 @@ provided by Linux on x86-64 architecture.
| 75 | fdatasync | ❌ | | 75 | fdatasync | ❌ |
| 76 | truncate | ✅ | | 76 | truncate | ✅ |
| 77 | ftruncate | ✅ | | 77 | ftruncate | ✅ |
| 78 | getdents | | | 78 | getdents | |
| 79 | getcwd | ✅ | | 79 | getcwd | ✅ |
| 80 | chdir | ✅ | | 80 | chdir | ✅ |
| 81 | fchdir | ✅ | | 81 | fchdir | ✅ |

View File

@ -28,7 +28,7 @@ use crate::syscall::{
fsync::sys_fsync, fsync::sys_fsync,
futex::sys_futex, futex::sys_futex,
getcwd::sys_getcwd, getcwd::sys_getcwd,
getdents64::sys_getdents64, getdents64::{sys_getdents, sys_getdents64},
getegid::sys_getegid, getegid::sys_getegid,
geteuid::sys_geteuid, geteuid::sys_geteuid,
getgid::sys_getgid, getgid::sys_getgid,
@ -184,6 +184,7 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_FSYNC = 74 => sys_fsync(args[..1]); SYS_FSYNC = 74 => sys_fsync(args[..1]);
SYS_TRUNCATE = 76 => sys_truncate(args[..2]); SYS_TRUNCATE = 76 => sys_truncate(args[..2]);
SYS_FTRUNCATE = 77 => sys_ftruncate(args[..2]); SYS_FTRUNCATE = 77 => sys_ftruncate(args[..2]);
SYS_GETDENTS = 78 => sys_getdents(args[..3]);
SYS_GETCWD = 79 => sys_getcwd(args[..2]); SYS_GETCWD = 79 => sys_getcwd(args[..2]);
SYS_CHDIR = 80 => sys_chdir(args[..1]); SYS_CHDIR = 80 => sys_chdir(args[..1]);
SYS_FCHDIR = 81 => sys_fchdir(args[..1]); SYS_FCHDIR = 81 => sys_fchdir(args[..1]);

View File

@ -15,6 +15,31 @@ use crate::{
util::write_bytes_to_user, util::write_bytes_to_user,
}; };
pub fn sys_getdents(fd: FileDesc, buf_addr: Vaddr, buf_len: usize) -> Result<SyscallReturn> {
debug!(
"fd = {}, buf_addr = 0x{:x}, buf_len = 0x{:x}",
fd, buf_addr, buf_len
);
let file = {
let current = current!();
let file_table = current.file_table().lock();
file_table.get_file(fd)?.clone()
};
let inode_handle = file
.downcast_ref::<InodeHandle>()
.ok_or(Error::with_message(Errno::EBADF, "not inode"))?;
if inode_handle.dentry().type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
let mut buffer = vec![0u8; buf_len];
let mut reader = DirentBufferReader::<Dirent>::new(&mut buffer); // Use the non-64-bit reader
let _ = inode_handle.readdir(&mut reader)?;
let read_len = reader.read_len();
write_bytes_to_user(buf_addr, &buffer[..read_len])?;
Ok(SyscallReturn::Return(read_len as _))
}
pub fn sys_getdents64(fd: FileDesc, buf_addr: Vaddr, buf_len: usize) -> Result<SyscallReturn> { pub fn sys_getdents64(fd: FileDesc, buf_addr: Vaddr, buf_len: usize) -> Result<SyscallReturn> {
debug!( debug!(
"fd = {}, buf_addr = 0x{:x}, buf_len = 0x{:x}", "fd = {}, buf_addr = 0x{:x}, buf_len = 0x{:x}",
@ -40,6 +65,16 @@ pub fn sys_getdents64(fd: FileDesc, buf_addr: Vaddr, buf_len: usize) -> Result<S
Ok(SyscallReturn::Return(read_len as _)) Ok(SyscallReturn::Return(read_len as _))
} }
/// The DirentSerializer can decide how to serialize the data.
trait DirentSerializer {
/// Create a DirentSerializer.
fn new(ino: u64, offset: u64, type_: InodeType, name: CString) -> Self;
/// Get the length of a directory entry.
fn len(&self) -> usize;
/// Try to serialize a directory entry into buffer.
fn serialize(&self, buf: &mut [u8]) -> Result<()>;
}
/// The Buffered DirentReader to visit the dir entry. /// The Buffered DirentReader to visit the dir entry.
/// The DirentSerializer T decides how to serialize the data. /// The DirentSerializer T decides how to serialize the data.
struct DirentBufferReader<'a, T: DirentSerializer> { struct DirentBufferReader<'a, T: DirentSerializer> {
@ -74,14 +109,63 @@ impl<'a, T: DirentSerializer> DirentVisitor for DirentBufferReader<'a, T> {
} }
} }
/// The DirentSerializer can decide how to serialize the data. #[derive(Debug)]
trait DirentSerializer { struct Dirent {
/// Create a DirentSerializer. inner: DirentInner,
fn new(ino: u64, offset: u64, type_: InodeType, name: CString) -> Self; name: CString,
/// Get the length of a directory entry. }
fn len(&self) -> usize;
/// Try to serialize a directory entry into buffer. #[repr(packed)]
fn serialize(&self, buf: &mut [u8]) -> Result<()>; #[derive(Debug, Clone, Copy)]
struct DirentInner {
d_ino: u64,
d_off: u64,
d_reclen: u16,
}
impl DirentSerializer for Dirent {
fn new(ino: u64, offset: u64, _type_: InodeType, name: CString) -> Self {
let d_reclen = {
let len =
core::mem::size_of::<Dirent64Inner>() + name.as_c_str().to_bytes_with_nul().len();
align_up(len, 8) as u16
};
Self {
inner: DirentInner {
d_ino: ino,
d_off: offset,
d_reclen,
},
name,
}
}
fn len(&self) -> usize {
self.inner.d_reclen as usize
}
fn serialize(&self, buf: &mut [u8]) -> Result<()> {
// Ensure buffer is large enough for the directory entry
if self.len() > buf.len() {
return_errno_with_message!(Errno::EINVAL, "buffer is too small");
}
let d_ino = self.inner.d_ino;
let d_off = self.inner.d_off;
let d_reclen = self.inner.d_reclen;
let items: [&[u8]; 4] = [
d_ino.as_bytes(),
d_off.as_bytes(),
d_reclen.as_bytes(),
self.name.as_c_str().to_bytes_with_nul(),
];
let mut offset = 0;
for item in items {
buf[offset..offset + item.len()].copy_from_slice(item);
offset += item.len();
}
Ok(())
}
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -1,11 +1,2 @@
GetdentsTest/0.VerifyEntries
GetdentsTest/0.VerifyPadding
GetdentsTest/0.SmallDir
GetdentsTest/0.LargeDir
GetdentsTest/0.PartialBuffer GetdentsTest/0.PartialBuffer
GetdentsTest/0.ProcSelfFd
GetdentsTest/0.NotDir
GetdentsTest/0.SeekResetsCursor
GetdentsTest/0.Issue128ProcSeekEnd
GetdentsTest/1.PartialBuffer GetdentsTest/1.PartialBuffer
GetdentsTest/1.ProcSelfFd