diff --git a/src/header/dirent/mod.rs b/src/header/dirent/mod.rs index f841bc48ad..d7992afa2c 100644 --- a/src/header/dirent/mod.rs +++ b/src/header/dirent/mod.rs @@ -4,8 +4,12 @@ #![deny(unsafe_op_in_unsafe_fn)] +use crate::{ + header::unistd::{SEEK_CUR, SEEK_SET}, + platform::types::{c_int, c_void, off_t, size_t, ssize_t}, +}; use alloc::{boxed::Box, vec::Vec}; -use core::{mem, ptr}; +use core::{mem, ptr, slice}; use crate::{ c_str::CStr, @@ -219,14 +223,18 @@ pub extern "C" fn fdopendir(fd: c_int) -> *mut DIR { } /// See . -// #[no_mangle] +#[no_mangle] pub extern "C" fn posix_getdents( fildes: c_int, buf: *mut c_void, nbyte: size_t, - flags: c_int, + _flags: c_int, ) -> ssize_t { - unimplemented!(); + let slice = unsafe { slice::from_raw_parts_mut(buf as *mut u8, nbyte) }; + + Sys::posix_getdents(fildes, slice) + .map(|s| s as ssize_t) + .or_minus_one_errno() } /// See . diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index dfeb2b4e3b..6a5027d797 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -1,4 +1,3 @@ -use crate::{header::errno::EOPNOTSUPP, io::Write, out::Out}; use core::{arch::asm, ptr}; use super::{types::*, Pal, ERRNO}; @@ -6,15 +5,16 @@ use crate::{ c_str::CStr, header::{ dirent::dirent, - errno::EINVAL, + errno::{EINVAL, EIO, EOPNOTSUPP}, fcntl::{AT_EMPTY_PATH, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_NOFOLLOW}, signal::SIGCHLD, sys_resource::{rlimit, rusage}, sys_stat::{stat, S_IFIFO}, sys_statvfs::statvfs, sys_time::{timeval, timezone}, - unistd::SEEK_SET, + unistd::{SEEK_CUR, SEEK_SET}, }, + io::Write, }; // use header::sys_times::tms; use crate::{ @@ -558,6 +558,32 @@ impl Pal for Sys { e_raw(unsafe { syscall!(PIPE2, fildes.as_mut_ptr(), flags) }).map(|_| ()) } + fn posix_getdents(fildes: c_int, buf: &mut [u8]) -> Result { + let current_offset = Self::lseek(fildes, 0, SEEK_CUR)? as u64; + let bytes_read = Self::getdents(fildes, buf, current_offset)?; + if bytes_read == 0 { + return Ok(0); + } + let mut bytes_processed = 0; + let mut next_offset = current_offset; + + while bytes_processed < bytes_read { + let remaining_slice = &buf[bytes_processed..]; + let (reclen, opaque_next) = + unsafe { Self::dent_reclen_offset(remaining_slice, bytes_processed) } + .ok_or(Errno(EIO))?; + if reclen == 0 { + return Err(Errno(EIO)); + } + + bytes_processed += reclen as usize; + next_offset = opaque_next; + } + + Self::lseek(fildes, next_offset as off_t, SEEK_SET)?; + Ok(bytes_read) + } + #[cfg(target_arch = "x86_64")] unsafe fn rlct_clone(stack: *mut usize) -> Result { let flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD; diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index f012a549fb..0c158a20c0 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -216,6 +216,8 @@ pub trait Pal { fn pipe2(fildes: Out<[c_int; 2]>, flags: c_int) -> Result<()>; + fn posix_getdents(fildes: c_int, buf: &mut [u8]) -> Result; + unsafe fn rlct_clone(stack: *mut usize) -> Result; unsafe fn rlct_kill(os_tid: pthread::OsTid, signal: usize) -> Result<()>; diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index c5c0ee4d16..c1b8325bef 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -36,7 +36,7 @@ use crate::{ sys_utsname::{utsname, UTSLENGTH}, sys_wait, time::timespec, - unistd::{F_OK, R_OK, W_OK, X_OK}, + unistd::{F_OK, R_OK, SEEK_CUR, SEEK_SET, W_OK, X_OK}, }, io::{self, prelude::*, BufReader}, out::Out, @@ -419,6 +419,7 @@ impl Pal for Sys { Ok(record_len.into()) } + fn dir_seek(_fd: c_int, _off: u64) -> Result<()> { // Redox getdents takes an explicit (opaque) offset, so this is a no-op. Ok(()) @@ -814,6 +815,32 @@ impl Pal for Sys { Ok(()) } + fn posix_getdents(fildes: c_int, buf: &mut [u8]) -> Result { + let current_offset = Self::lseek(fildes, 0, SEEK_CUR)? as u64; + let bytes_read = Self::getdents(fildes, buf, current_offset)?; + if bytes_read == 0 { + return Ok(0); + } + let mut bytes_processed = 0; + let mut next_offset = current_offset; + + while bytes_processed < bytes_read { + let remaining_slice = &buf[bytes_processed..]; + let (reclen, opaque_next) = + unsafe { Self::dent_reclen_offset(remaining_slice, bytes_processed) } + .ok_or(Errno(EIO))?; + if reclen == 0 { + return Err(Errno(EIO)); + } + + bytes_processed += reclen as usize; + next_offset = opaque_next; + } + + Self::lseek(fildes, next_offset as off_t, SEEK_SET)?; + Ok(bytes_read) + } + unsafe fn rlct_clone(stack: *mut usize) -> Result { let _guard = CLONE_LOCK.read(); let res = clone::rlct_clone_impl(stack); diff --git a/tests/Makefile b/tests/Makefile index c63daa4e8a..38b29de8f0 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -189,6 +189,7 @@ DYNAMIC_ONLY_NAMES=\ NAMES=\ $(EXPECT_NAMES) \ dirent/main \ + dirent/posix_getdents \ kill-waitpid \ net/if \ pty/forkpty \ diff --git a/tests/dirent/posix_getdents.c b/tests/dirent/posix_getdents.c new file mode 100644 index 0000000000..9269035e5a --- /dev/null +++ b/tests/dirent/posix_getdents.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +#define BUFFER_SIZE 4096 + +void read_and_print_directory(int fd) { + char buffer[BUFFER_SIZE]; + long nread; + long bpos; + struct dirent *d; + + for (;;) { + nread = posix_getdents(fd, buffer, BUFFER_SIZE, 0); + ERROR_IF(posix_getdents, nread, == -1); + UNEXP_IF(posix_getdents, nread, < 0); + + if (nread == 0) { + break; + } + + for (bpos = 0; bpos < nread;) { + d = (struct dirent *) (buffer + bpos); + printf(" ino = %-10lu name = %s\n", (unsigned long)d->d_ino, d->d_name); + bpos += d->d_reclen; + } + } +} + +int main(void) { + int fd = open("example_dir/", O_RDONLY | O_DIRECTORY); + ERROR_IF(open, fd, == -1); + read_and_print_directory(fd); + + off_t seek_result = lseek(fd, 0, SEEK_SET); + ERROR_IF(lseek, seek_result, == -1); + read_and_print_directory(fd); + + close(fd); +}