diff --git a/src/libos/src/fs/file_ops/dirent.rs b/src/libos/src/fs/file_ops/dirent.rs index 05f387cf..dd32c25b 100644 --- a/src/libos/src/fs/file_ops/dirent.rs +++ b/src/libos/src/fs/file_ops/dirent.rs @@ -1,72 +1,21 @@ use super::*; -#[repr(packed)] // Don't use 'C'. Or its size will align up to 8 bytes. -struct LinuxDirent64 { - /// Inode number - ino: u64, - /// Offset to next structure - offset: u64, - /// Size of this dirent - reclen: u16, - /// File type - type_: u8, - /// Filename (null-terminated) - name: [u8; 0], -} - -struct DirentBufWriter<'a> { - buf: &'a mut [u8], - rest_size: usize, - written_size: usize, -} - -impl<'a> DirentBufWriter<'a> { - unsafe fn new(buf: &'a mut [u8]) -> Self { - let rest_size = buf.len(); - DirentBufWriter { - buf, - rest_size, - written_size: 0, - } - } - fn try_write(&mut self, inode: u64, type_: u8, name: &str) -> Result<()> { - let len = ::core::mem::size_of::() + name.len() + 1; - let len = (len + 7) / 8 * 8; // align up - if self.rest_size < len { - return_errno!(EINVAL, "the given buffer is too small"); - } - let dent = LinuxDirent64 { - ino: inode, - offset: 0, - reclen: len as u16, - type_, - name: [], - }; - unsafe { - let ptr = self.buf.as_mut_ptr().add(self.written_size) as *mut LinuxDirent64; - ptr.write(dent); - let name_ptr = ptr.add(1) as _; - write_cstr(name_ptr, name); - } - self.rest_size -= len; - self.written_size += len; - Ok(()) - } -} - -/// Write a Rust string to C string -unsafe fn write_cstr(ptr: *mut u8, s: &str) { - ptr.copy_from(s.as_ptr(), s.len()); - ptr.add(s.len()).write(0); -} - pub fn do_getdents64(fd: FileDesc, buf: &mut [u8]) -> Result { + getdents_common::(fd, buf) +} + +pub fn do_getdents(fd: FileDesc, buf: &mut [u8]) -> Result { + getdents_common::<()>(fd, buf) +} + +fn getdents_common(fd: FileDesc, buf: &mut [u8]) -> Result { debug!( - "getdents64: fd: {}, buf: {:?}, buf_size: {}", + "getdents: fd: {}, buf: {:?}, buf_size: {}", fd, buf.as_ptr(), - buf.len() + buf.len(), ); + let file_ref = current!().file(fd)?; let info = file_ref.metadata()?; if info.type_ != FileType::Dir { @@ -85,7 +34,8 @@ pub fn do_getdents64(fd: FileDesc, buf: &mut [u8]) -> Result { Ok(name) => name, }; // TODO: get ino from dirent - if let Err(e) = writer.try_write(0, 0, &name) { + let dirent = LinuxDirent::::new(1, &name); + if let Err(e) = writer.try_write(&dirent, &name) { file_ref.seek(SeekFrom::Current(-1))?; if writer.written_size == 0 { return Err(e); @@ -96,3 +46,96 @@ pub fn do_getdents64(fd: FileDesc, buf: &mut [u8]) -> Result { } Ok(writer.written_size) } + +#[repr(packed)] // Don't use 'C'. Or its size will align up to 8 bytes. +struct LinuxDirent { + /// Inode number + ino: u64, + /// Offset to next structure + offset: u64, + /// Size of this dirent + reclen: u16, + /// File type + type_: T, + /// Filename (null-terminated) + name: [u8; 0], +} + +impl LinuxDirent { + fn new(ino: u64, name: &str) -> Self { + let ori_len = ::core::mem::size_of::>() + name.len() + 1; + let len = align_up(ori_len, 8); // align up to 8 bytes + Self { + ino, + offset: 0, + reclen: len as u16, + type_: Default::default(), + name: [], + } + } + + fn len(&self) -> usize { + self.reclen as usize + } +} + +impl Copy for LinuxDirent {} + +impl Clone for LinuxDirent { + fn clone(&self) -> Self { + Self { + ino: self.ino, + offset: self.offset, + reclen: self.reclen, + type_: self.type_, + name: self.name, + } + } +} + +trait DirentType {} + +impl DirentType for u8 {} +impl DirentType for () {} + +struct DirentBufWriter<'a> { + buf: &'a mut [u8], + rest_size: usize, + written_size: usize, +} + +impl<'a> DirentBufWriter<'a> { + unsafe fn new(buf: &'a mut [u8]) -> Self { + let rest_size = buf.len(); + DirentBufWriter { + buf, + rest_size, + written_size: 0, + } + } + + fn try_write( + &mut self, + dirent: &LinuxDirent, + name: &str, + ) -> Result<()> { + if self.rest_size < dirent.len() { + return_errno!(EINVAL, "the given buffer is too small"); + } + unsafe { + let ptr = self.buf.as_mut_ptr().add(self.written_size) as *mut LinuxDirent; + ptr.write(*dirent); + let name_ptr = ptr.add(1) as _; + write_cstr(name_ptr, name); + } + self.rest_size -= dirent.len(); + self.written_size += dirent.len(); + Ok(()) + } +} + +/// Write a Rust string to C string +unsafe fn write_cstr(ptr: *mut u8, s: &str) { + ptr.copy_from(s.as_ptr(), s.len()); + ptr.add(s.len()).write(0); +} diff --git a/src/libos/src/fs/file_ops/mod.rs b/src/libos/src/fs/file_ops/mod.rs index 0171443a..b976e8fc 100644 --- a/src/libos/src/fs/file_ops/mod.rs +++ b/src/libos/src/fs/file_ops/mod.rs @@ -6,7 +6,7 @@ pub use self::access::{do_faccessat, AccessibilityCheckFlags, AccessibilityCheck pub use self::chmod::{do_fchmod, do_fchmodat, ChmodFlags, FileMode}; pub use self::chown::{do_fchown, do_fchownat, ChownFlags}; pub use self::close::do_close; -pub use self::dirent::do_getdents64; +pub use self::dirent::{do_getdents, do_getdents64}; pub use self::dup::{do_dup, do_dup2, do_dup3}; pub use self::fcntl::{do_fcntl, FcntlCmd}; pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags}; diff --git a/src/libos/src/fs/syscalls.rs b/src/libos/src/fs/syscalls.rs index 665736c7..703e4b2b 100644 --- a/src/libos/src/fs/syscalls.rs +++ b/src/libos/src/fs/syscalls.rs @@ -248,6 +248,15 @@ pub fn do_getdents64(fd: FileDesc, buf: *mut u8, buf_size: usize) -> Result Result { + let safe_buf = { + from_user::check_mut_array(buf, buf_size)?; + unsafe { std::slice::from_raw_parts_mut(buf, buf_size) } + }; + let len = file_ops::do_getdents(fd, safe_buf)?; + Ok(len as isize) +} + pub fn do_sync() -> Result { fs_ops::do_sync()?; Ok(0) diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index ca39115b..51fd3e11 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -24,12 +24,12 @@ use crate::exception::do_handle_exception; use crate::fs::{ do_access, do_chdir, do_chmod, do_chown, do_close, do_dup, do_dup2, do_dup3, do_eventfd, do_eventfd2, do_faccessat, do_fchmod, do_fchmodat, do_fchown, do_fchownat, do_fcntl, - do_fdatasync, do_fstat, do_fstatat, do_fsync, do_ftruncate, do_getcwd, do_getdents64, do_ioctl, - do_lchown, do_link, do_linkat, do_lseek, do_lstat, do_mkdir, do_mkdirat, do_open, do_openat, - do_pipe, do_pipe2, do_pread, do_pwrite, do_read, do_readlink, do_readlinkat, do_readv, - do_rename, do_renameat, do_rmdir, do_sendfile, do_stat, do_symlink, do_symlinkat, do_sync, - do_truncate, do_unlink, do_unlinkat, do_write, do_writev, iovec_t, File, FileDesc, FileRef, - HostStdioFds, Stat, + do_fdatasync, do_fstat, do_fstatat, do_fsync, do_ftruncate, do_getcwd, do_getdents, + do_getdents64, do_ioctl, do_lchown, do_link, do_linkat, do_lseek, do_lstat, do_mkdir, + do_mkdirat, do_open, do_openat, do_pipe, do_pipe2, do_pread, do_pwrite, do_read, do_readlink, + do_readlinkat, do_readv, do_rename, do_renameat, do_rmdir, do_sendfile, do_stat, do_symlink, + do_symlinkat, do_sync, do_truncate, do_unlink, do_unlinkat, do_write, do_writev, iovec_t, File, + FileDesc, FileRef, HostStdioFds, Stat, }; use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t}; use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_t}; @@ -162,7 +162,7 @@ macro_rules! process_syscall_table_with_callback { (Fdatasync = 75) => do_fdatasync(fd: FileDesc), (Truncate = 76) => do_truncate(path: *const i8, len: usize), (Ftruncate = 77) => do_ftruncate(fd: FileDesc, len: usize), - (Getdents = 78) => handle_unsupported(), + (Getdents = 78) => do_getdents(fd: FileDesc, buf: *mut u8, buf_size: usize), (Getcwd = 79) => do_getcwd(buf: *mut u8, size: usize), (Chdir = 80) => do_chdir(path: *const i8), (Fchdir = 81) => handle_unsupported(), diff --git a/test/readdir/main.c b/test/readdir/main.c index ed55cc39..7c6e6fc0 100644 --- a/test/readdir/main.c +++ b/test/readdir/main.c @@ -1,6 +1,8 @@ #include #include +#include #include +#include #include #include #include "test_fs.h" @@ -32,7 +34,7 @@ static int test_readdir() { return 0; } -static int test_getdents_with_big_enough_buffer() { +static int getdents_with_big_enough_buffer(bool use_explicit_syscall) { int fd, len; char buf[64]; @@ -41,7 +43,11 @@ static int test_getdents_with_big_enough_buffer() { THROW_ERROR("failed to open directory"); } while (1) { - len = getdents(fd, (struct dirent *)buf, sizeof(buf)); + if (use_explicit_syscall) { + len = syscall(__NR_getdents, fd, buf, sizeof(buf)); + } else { + len = getdents(fd, (struct dirent *)buf, sizeof(buf)); + } if (len < 0) { close(fd); THROW_ERROR("failed to call getdents"); @@ -54,7 +60,17 @@ static int test_getdents_with_big_enough_buffer() { return 0; } -static int test_getdents_with_too_small_buffer() { +static int test_getdents_with_big_enough_buffer() { + bool use_explicit_syscall = false; + return getdents_with_big_enough_buffer(use_explicit_syscall); +} + +static int test_getdents_via_explicit_syscall_with_big_enough_buffer() { + bool use_explicit_syscall = true; + return getdents_with_big_enough_buffer(use_explicit_syscall); +} + +static int getdents_with_too_small_buffer(bool use_explicit_syscall) { int fd, len; char buf[4]; @@ -62,7 +78,11 @@ static int test_getdents_with_too_small_buffer() { if (fd < 0) { THROW_ERROR("failed to open directory"); } - len = getdents(fd, (struct dirent *)buf, sizeof(buf)); + if (use_explicit_syscall) { + len = syscall(__NR_getdents, fd, buf, sizeof(buf)); + } else { + len = getdents(fd, (struct dirent *)buf, sizeof(buf)); + } if (len >= 0 || errno != EINVAL) { close(fd); THROW_ERROR("failed to call getdents with small buffer"); @@ -71,6 +91,16 @@ static int test_getdents_with_too_small_buffer() { return 0; } +static int test_getdents_with_too_small_buffer() { + bool use_explicit_syscall = false; + return getdents_with_too_small_buffer(use_explicit_syscall); +} + +static int test_getdents_via_explicit_syscall_with_too_small_buffer() { + bool use_explicit_syscall = true; + return getdents_with_too_small_buffer(use_explicit_syscall); +} + // ============================================================================ // Test suite main // ============================================================================ @@ -78,7 +108,9 @@ static int test_getdents_with_too_small_buffer() { static test_case_t test_cases[] = { TEST_CASE(test_readdir), TEST_CASE(test_getdents_with_big_enough_buffer), + TEST_CASE(test_getdents_via_explicit_syscall_with_big_enough_buffer), TEST_CASE(test_getdents_with_too_small_buffer), + TEST_CASE(test_getdents_via_explicit_syscall_with_too_small_buffer), }; int main() {