Add getdents syscall
This commit is contained in:
parent
4d4caa2265
commit
9bf2a77e16
@ -1,72 +1,21 @@
|
|||||||
use super::*;
|
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::<LinuxDirent64>() + 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<usize> {
|
pub fn do_getdents64(fd: FileDesc, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
getdents_common::<u8>(fd, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn do_getdents(fd: FileDesc, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
getdents_common::<()>(fd, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getdents_common<T: DirentType + Copy + Default>(fd: FileDesc, buf: &mut [u8]) -> Result<usize> {
|
||||||
debug!(
|
debug!(
|
||||||
"getdents64: fd: {}, buf: {:?}, buf_size: {}",
|
"getdents: fd: {}, buf: {:?}, buf_size: {}",
|
||||||
fd,
|
fd,
|
||||||
buf.as_ptr(),
|
buf.as_ptr(),
|
||||||
buf.len()
|
buf.len(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let file_ref = current!().file(fd)?;
|
let file_ref = current!().file(fd)?;
|
||||||
let info = file_ref.metadata()?;
|
let info = file_ref.metadata()?;
|
||||||
if info.type_ != FileType::Dir {
|
if info.type_ != FileType::Dir {
|
||||||
@ -85,7 +34,8 @@ pub fn do_getdents64(fd: FileDesc, buf: &mut [u8]) -> Result<usize> {
|
|||||||
Ok(name) => name,
|
Ok(name) => name,
|
||||||
};
|
};
|
||||||
// TODO: get ino from dirent
|
// TODO: get ino from dirent
|
||||||
if let Err(e) = writer.try_write(0, 0, &name) {
|
let dirent = LinuxDirent::<T>::new(1, &name);
|
||||||
|
if let Err(e) = writer.try_write(&dirent, &name) {
|
||||||
file_ref.seek(SeekFrom::Current(-1))?;
|
file_ref.seek(SeekFrom::Current(-1))?;
|
||||||
if writer.written_size == 0 {
|
if writer.written_size == 0 {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
@ -96,3 +46,96 @@ pub fn do_getdents64(fd: FileDesc, buf: &mut [u8]) -> Result<usize> {
|
|||||||
}
|
}
|
||||||
Ok(writer.written_size)
|
Ok(writer.written_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(packed)] // Don't use 'C'. Or its size will align up to 8 bytes.
|
||||||
|
struct LinuxDirent<T: DirentType + Copy + Default> {
|
||||||
|
/// 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<T: DirentType + Copy + Default> LinuxDirent<T> {
|
||||||
|
fn new(ino: u64, name: &str) -> Self {
|
||||||
|
let ori_len = ::core::mem::size_of::<LinuxDirent<T>>() + 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<T: DirentType + Copy + Default> Copy for LinuxDirent<T> {}
|
||||||
|
|
||||||
|
impl<T: DirentType + Copy + Default> Clone for LinuxDirent<T> {
|
||||||
|
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<T: DirentType + Copy + Default>(
|
||||||
|
&mut self,
|
||||||
|
dirent: &LinuxDirent<T>,
|
||||||
|
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<T>;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
@ -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::chmod::{do_fchmod, do_fchmodat, ChmodFlags, FileMode};
|
||||||
pub use self::chown::{do_fchown, do_fchownat, ChownFlags};
|
pub use self::chown::{do_fchown, do_fchownat, ChownFlags};
|
||||||
pub use self::close::do_close;
|
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::dup::{do_dup, do_dup2, do_dup3};
|
||||||
pub use self::fcntl::{do_fcntl, FcntlCmd};
|
pub use self::fcntl::{do_fcntl, FcntlCmd};
|
||||||
pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags};
|
pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags};
|
||||||
|
@ -248,6 +248,15 @@ pub fn do_getdents64(fd: FileDesc, buf: *mut u8, buf_size: usize) -> Result<isiz
|
|||||||
Ok(len as isize)
|
Ok(len as isize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn do_getdents(fd: FileDesc, buf: *mut u8, buf_size: usize) -> Result<isize> {
|
||||||
|
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<isize> {
|
pub fn do_sync() -> Result<isize> {
|
||||||
fs_ops::do_sync()?;
|
fs_ops::do_sync()?;
|
||||||
Ok(0)
|
Ok(0)
|
||||||
|
@ -24,12 +24,12 @@ use crate::exception::do_handle_exception;
|
|||||||
use crate::fs::{
|
use crate::fs::{
|
||||||
do_access, do_chdir, do_chmod, do_chown, do_close, do_dup, do_dup2, do_dup3, do_eventfd,
|
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_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_fdatasync, do_fstat, do_fstatat, do_fsync, do_ftruncate, do_getcwd, do_getdents,
|
||||||
do_lchown, do_link, do_linkat, do_lseek, do_lstat, do_mkdir, do_mkdirat, do_open, do_openat,
|
do_getdents64, do_ioctl, do_lchown, do_link, do_linkat, do_lseek, do_lstat, do_mkdir,
|
||||||
do_pipe, do_pipe2, do_pread, do_pwrite, do_read, do_readlink, do_readlinkat, do_readv,
|
do_mkdirat, do_open, do_openat, do_pipe, do_pipe2, do_pread, do_pwrite, do_read, do_readlink,
|
||||||
do_rename, do_renameat, do_rmdir, do_sendfile, do_stat, do_symlink, do_symlinkat, do_sync,
|
do_readlinkat, do_readv, do_rename, do_renameat, do_rmdir, do_sendfile, do_stat, do_symlink,
|
||||||
do_truncate, do_unlink, do_unlinkat, do_write, do_writev, iovec_t, File, FileDesc, FileRef,
|
do_symlinkat, do_sync, do_truncate, do_unlink, do_unlinkat, do_write, do_writev, iovec_t, File,
|
||||||
HostStdioFds, Stat,
|
FileDesc, FileRef, HostStdioFds, Stat,
|
||||||
};
|
};
|
||||||
use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t};
|
use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t};
|
||||||
use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_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),
|
(Fdatasync = 75) => do_fdatasync(fd: FileDesc),
|
||||||
(Truncate = 76) => do_truncate(path: *const i8, len: usize),
|
(Truncate = 76) => do_truncate(path: *const i8, len: usize),
|
||||||
(Ftruncate = 77) => do_ftruncate(fd: FileDesc, 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),
|
(Getcwd = 79) => do_getcwd(buf: *mut u8, size: usize),
|
||||||
(Chdir = 80) => do_chdir(path: *const i8),
|
(Chdir = 80) => do_chdir(path: *const i8),
|
||||||
(Fchdir = 81) => handle_unsupported(),
|
(Fchdir = 81) => handle_unsupported(),
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include "test_fs.h"
|
#include "test_fs.h"
|
||||||
@ -32,7 +34,7 @@ static int test_readdir() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int test_getdents_with_big_enough_buffer() {
|
static int getdents_with_big_enough_buffer(bool use_explicit_syscall) {
|
||||||
int fd, len;
|
int fd, len;
|
||||||
char buf[64];
|
char buf[64];
|
||||||
|
|
||||||
@ -41,7 +43,11 @@ static int test_getdents_with_big_enough_buffer() {
|
|||||||
THROW_ERROR("failed to open directory");
|
THROW_ERROR("failed to open directory");
|
||||||
}
|
}
|
||||||
while (1) {
|
while (1) {
|
||||||
|
if (use_explicit_syscall) {
|
||||||
|
len = syscall(__NR_getdents, fd, buf, sizeof(buf));
|
||||||
|
} else {
|
||||||
len = getdents(fd, (struct dirent *)buf, sizeof(buf));
|
len = getdents(fd, (struct dirent *)buf, sizeof(buf));
|
||||||
|
}
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
close(fd);
|
close(fd);
|
||||||
THROW_ERROR("failed to call getdents");
|
THROW_ERROR("failed to call getdents");
|
||||||
@ -54,7 +60,17 @@ static int test_getdents_with_big_enough_buffer() {
|
|||||||
return 0;
|
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;
|
int fd, len;
|
||||||
char buf[4];
|
char buf[4];
|
||||||
|
|
||||||
@ -62,7 +78,11 @@ static int test_getdents_with_too_small_buffer() {
|
|||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
THROW_ERROR("failed to open directory");
|
THROW_ERROR("failed to open directory");
|
||||||
}
|
}
|
||||||
|
if (use_explicit_syscall) {
|
||||||
|
len = syscall(__NR_getdents, fd, buf, sizeof(buf));
|
||||||
|
} else {
|
||||||
len = getdents(fd, (struct dirent *)buf, sizeof(buf));
|
len = getdents(fd, (struct dirent *)buf, sizeof(buf));
|
||||||
|
}
|
||||||
if (len >= 0 || errno != EINVAL) {
|
if (len >= 0 || errno != EINVAL) {
|
||||||
close(fd);
|
close(fd);
|
||||||
THROW_ERROR("failed to call getdents with small buffer");
|
THROW_ERROR("failed to call getdents with small buffer");
|
||||||
@ -71,6 +91,16 @@ static int test_getdents_with_too_small_buffer() {
|
|||||||
return 0;
|
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
|
// Test suite main
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -78,7 +108,9 @@ static int test_getdents_with_too_small_buffer() {
|
|||||||
static test_case_t test_cases[] = {
|
static test_case_t test_cases[] = {
|
||||||
TEST_CASE(test_readdir),
|
TEST_CASE(test_readdir),
|
||||||
TEST_CASE(test_getdents_with_big_enough_buffer),
|
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_with_too_small_buffer),
|
||||||
|
TEST_CASE(test_getdents_via_explicit_syscall_with_too_small_buffer),
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
Loading…
Reference in New Issue
Block a user