From c0e7af52890075afbdbe0d42428e1deeb8aaa854 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 8 Sep 2024 22:21:24 +0200 Subject: [PATCH] Backwards-compatibly rewrite getdents to use special syscall. --- Cargo.lock | 69 ++++++--- Cargo.toml | 2 +- Makefile | 20 ++- cbindgen.globdefs.toml | 13 ++ src/error.rs | 27 ++++ src/header/dirent/mod.rs | 205 +++++++++++++++++--------- src/header/fcntl/cbindgen.toml | 4 - src/header/signal/cbindgen.toml | 4 - src/header/stdlib/cbindgen.toml | 3 - src/header/sys_epoll/cbindgen.toml | 5 - src/header/sys_ioctl/cbindgen.toml | 4 - src/header/sys_mman/cbindgen.toml | 4 - src/header/sys_resource/cbindgen.toml | 4 - src/header/sys_socket/cbindgen.toml | 4 - src/header/termios/cbindgen.toml | 4 - src/header/time/cbindgen.toml | 4 - src/header/unistd/cbindgen.toml | 4 - src/lib.rs | 1 + src/platform/linux/mod.rs | 15 +- src/platform/pal/mod.rs | 8 +- src/platform/redox/mod.rs | 163 ++++++++++---------- tests/dirent/main.c | 2 + 22 files changed, 338 insertions(+), 231 deletions(-) create mode 100644 cbindgen.globdefs.toml diff --git a/Cargo.lock b/Cargo.lock index 1710b7ff4a..6f2ce4c5c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.5" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -89,9 +92,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -182,9 +185,9 @@ version = "0.1.0" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libredox" @@ -261,9 +264,12 @@ version = "0.1.0" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" @@ -276,9 +282,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -384,9 +390,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags", ] @@ -491,6 +497,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "subtle" version = "2.6.1" @@ -499,9 +511,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.71" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -516,9 +528,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-width" @@ -528,9 +540,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "winapi" @@ -553,3 +565,24 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index e3ebeb48ae..cc46f45476 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ features = ["c_api"] sc = "0.2.3" [target.'cfg(target_os = "redox")'.dependencies] -redox_syscall = "0.5.3" +redox_syscall = "0.5.4" redox-rt = { path = "redox-rt" } redox-path = "0.2" redox_event = { git = "https://gitlab.redox-os.org/redox-os/event.git", default-features = false, features = ["redox_syscall"] } diff --git a/Makefile b/Makefile index 883de4fc5f..228389a196 100644 --- a/Makefile +++ b/Makefile @@ -62,14 +62,22 @@ BUILTINS_VERSION=0.1.70 all: | headers libs headers: $(HEADERS_DEPS) + globdefs="$$(cat cbindgen.globdefs.toml)"; \ for header in $(HEADERS_UNPARSED); do \ + echo "Header $$header"; \ if test -f "src/header/$$header/cbindgen.toml"; then \ - out=`echo "$$header" | sed 's/_/\//g'`; \ - out="$(TARGET_HEADERS)/$$out.h"; \ - cbindgen --output "$$out" \ - --config="src/header/$$header/cbindgen.toml" \ - "src/header/$$header/mod.rs"; \ - sed -i "s/va_list __valist/.../g" "$$out"; \ + out=`echo "$$header" | sed 's/_/\//g'`; \ + out="$(TARGET_HEADERS)/$$out.h"; \ + header_path="src/header/$$header/cbindgen.toml"; \ + if ( grep -F '[defines]' "$$header_path" >/dev/null ); then \ + config=$$(cat "$$header_path" | sed "s/\[defines\]/$$globdefs/"); \ + else \ + config=$$(cat "$$header_path" cbindgen.globdefs.toml); \ + fi; \ + echo "$$config" \ + | tee "/tmp/a$$header.log" \ + | cbindgen "src/header/$$header/mod.rs" --config=/dev/stdin --output "$$out"; \ + sed -i "s/va_list __valist/.../g" "$$out"; \ fi \ done diff --git a/cbindgen.globdefs.toml b/cbindgen.globdefs.toml new file mode 100644 index 0000000000..775d07bdcb --- /dev/null +++ b/cbindgen.globdefs.toml @@ -0,0 +1,13 @@ + +# needs a leading newline +[defines] +"target_os=redox" = "__redox__" +"target_os=linux" = "__linux__" +"target_pointer_width=64" = "__LP64__" +"target_pointer_width=32" = "__LP32__" +"target_arch=x86" = "__i386__" +"target_arch=x86_64" = "__x86_64__" +"target_arch=aarch64" = "__aarch64__" + +# XXX: silences a warning +"feature = no_std" = "__relibc__" diff --git a/src/error.rs b/src/error.rs index bd3da96df0..42907f5f98 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,5 @@ +use alloc::boxed::Box; + use crate::{header::errno::STR_ERROR, platform::types::c_int}; /// Positive error codes (EINVAL, not -EINVAL). @@ -52,3 +54,28 @@ impl> ResultExt for Result { } } } +pub trait ResultExtPtrMut { + fn or_errno_null_mut(self) -> *mut T; +} +impl ResultExtPtrMut for Result<*mut T, Errno> { + fn or_errno_null_mut(self) -> *mut T { + match self { + Self::Ok(ptr) => ptr, + Self::Err(Errno(errno)) => { + crate::platform::ERRNO.set(errno); + core::ptr::null_mut() + } + } + } +} +impl ResultExtPtrMut for Result, Errno> { + fn or_errno_null_mut(self) -> *mut T { + match self { + Self::Ok(ptr) => Box::into_raw(ptr), + Self::Err(Errno(errno)) => { + crate::platform::ERRNO.set(errno); + core::ptr::null_mut() + } + } + } +} diff --git a/src/header/dirent/mod.rs b/src/header/dirent/mod.rs index 8f9220928b..436ac13575 100644 --- a/src/header/dirent/mod.rs +++ b/src/header/dirent/mod.rs @@ -1,29 +1,114 @@ //! dirent implementation following http://pubs.opengroup.org/onlinepubs/009695399/basedefs/dirent.h.html -use alloc::boxed::Box; +use alloc::{boxed::Box, vec::Vec}; use core::{mem, ptr}; use crate::{ c_str::CStr, c_vec::CVec, + error::{Errno, ResultExtPtrMut}, fs::File, - header::{errno, fcntl, stdlib, string}, - io::{Seek, SeekFrom}, + header::{fcntl, stdlib, string}, platform::{self, types::*, Pal, Sys}, }; -const DIR_BUF_SIZE: usize = mem::size_of::() * 3; +use super::errno::{EINVAL, EIO, ENOMEM}; -// No repr(C) needed, C won't see the content +const INITIAL_BUFSIZE: usize = 512; + +// No repr(C) needed, as this is a completely opaque struct. Being accessed as a pointer, in C it's +// just defined as `struct DIR`. pub struct DIR { file: File, - buf: [c_char; DIR_BUF_SIZE], - // index and len are specified in bytes - index: usize, - len: usize, + buf: Vec, + buf_offset: usize, // The last value of d_off, used by telldir - offset: usize, + opaque_offset: u64, +} +impl DIR { + pub fn new(path: CStr) -> Result, Errno> { + Ok(Box::new(Self { + file: File::open( + path, + fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC, + )?, + buf: Vec::with_capacity(INITIAL_BUFSIZE), + buf_offset: 0, + opaque_offset: 0, + })) + } + fn next_dirent(&mut self) -> Result<*mut dirent, Errno> { + let mut this_dent = self.buf.get(self.buf_offset..).ok_or(Errno(EIO))?; + if this_dent.is_empty() { + let size = loop { + self.buf.resize(self.buf.capacity(), 0_u8); + // TODO: uninitialized memory? + match Sys::getdents(*self.file, &mut self.buf, self.opaque_offset) { + Ok(size) => break size, + Err(Errno(EINVAL)) => { + self.buf + .try_reserve_exact(self.buf.len()) + .map_err(|_| Errno(ENOMEM))?; + continue; + } + Err(Errno(other)) => return Err(Errno(other)), + } + }; + self.buf.truncate(size); + self.buf_offset = 0; + + if size == 0 { + return Ok(core::ptr::null_mut()); + } + this_dent = &self.buf; + } + let (this_reclen, this_next_opaque) = + unsafe { Sys::dent_reclen_offset(this_dent, self.buf_offset).ok_or(Errno(EIO))? }; + + //println!("CDENT {} {}+{}", self.opaque_offset, self.buf_offset, this_reclen); + + let next_off = self + .buf_offset + .checked_add(usize::from(this_reclen)) + .ok_or(Errno(EIO))?; + if next_off > self.buf.len() { + return Err(Errno(EIO)); + } + if this_dent.len() < usize::from(this_reclen) { + // Don't want memory corruption if a scheme is adversarial. + return Err(Errno(EIO)); + } + let dent_ptr = this_dent.as_ptr() as *mut dirent; + + self.opaque_offset = this_next_opaque; + self.buf_offset = next_off; + Ok(dent_ptr) + } + fn seek(&mut self, off: u64) { + let Ok(_) = Sys::dir_seek(*self.file, off) else { + return; + }; + self.buf.clear(); + self.buf_offset = 0; + self.opaque_offset = off; + } + fn rewind(&mut self) { + self.opaque_offset = 0; + let Ok(_) = Sys::dir_seek(*self.file, 0) else { + return; + }; + self.buf.clear(); + self.buf_offset = 0; + self.opaque_offset = 0; + } + fn close(mut self) -> c_int { + // Reference files aren't closed when dropped + self.file.reference = true; + + // TODO: result + Sys::close(*self.file) + } } #[repr(C)] @@ -36,68 +121,50 @@ pub struct dirent { pub d_name: [c_char; 256], } +#[cfg(target_os = "redox")] +const _: () = { + use core::mem::{offset_of, size_of}; + use syscall::dirent::DirentHeader; + + if offset_of!(dirent, d_ino) != offset_of!(DirentHeader, inode) { + panic!("struct dirent layout mismatch (inode)"); + } + if offset_of!(dirent, d_off) != offset_of!(DirentHeader, next_opaque_id) { + panic!("struct dirent layout mismatch (inode)"); + } + if offset_of!(dirent, d_reclen) != offset_of!(DirentHeader, record_len) { + panic!("struct dirent layout mismatch (len)"); + } + if offset_of!(dirent, d_type) != offset_of!(DirentHeader, kind) { + panic!("struct dirent layout mismatch (kind)"); + } + if offset_of!(dirent, d_name) != size_of::() { + panic!("struct dirent layout mismatch (name)"); + } +}; + #[no_mangle] pub unsafe extern "C" fn opendir(path: *const c_char) -> *mut DIR { let path = CStr::from_ptr(path); - let file = match File::open( - path, - fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC, - ) { - Ok(file) => file, - Err(_) => return ptr::null_mut(), - }; - Box::into_raw(Box::new(DIR { - file, - buf: [0; DIR_BUF_SIZE], - index: 0, - len: 0, - offset: 0, - })) + DIR::new(path).or_errno_null_mut() } #[no_mangle] -pub unsafe extern "C" fn closedir(dir: *mut DIR) -> c_int { - let mut dir = Box::from_raw(dir); - - let ret = Sys::close(*dir.file); - - // Reference files aren't closed when dropped - dir.file.reference = true; - - ret +pub extern "C" fn closedir(dir: Box) -> c_int { + dir.close() } #[no_mangle] -pub unsafe extern "C" fn dirfd(dir: *mut DIR) -> c_int { - *((*dir).file) +pub extern "C" fn dirfd(dir: &mut DIR) -> c_int { + *dir.file } #[no_mangle] -pub unsafe extern "C" fn readdir(dir: *mut DIR) -> *mut dirent { - if (*dir).index >= (*dir).len { - let read = Sys::getdents( - *(*dir).file, - (*dir).buf.as_mut_ptr() as *mut dirent, - (*dir).buf.len(), - ); - if read <= 0 { - if read != 0 && read != -errno::ENOENT { - platform::ERRNO.set(-read); - } - return ptr::null_mut(); - } - - (*dir).index = 0; - (*dir).len = read as usize; - } - - let ptr = (*dir).buf.as_mut_ptr().add((*dir).index) as *mut dirent; - - (*dir).offset = (*ptr).d_off as usize; - (*dir).index += (*ptr).d_reclen as usize; - ptr +pub extern "C" fn readdir(dir: &mut DIR) -> *mut dirent { + dir.next_dirent().or_errno_null_mut() } + // #[no_mangle] pub extern "C" fn readdir_r( _dir: *mut DIR, @@ -108,19 +175,19 @@ pub extern "C" fn readdir_r( } #[no_mangle] -pub unsafe extern "C" fn telldir(dir: *mut DIR) -> c_long { - (*dir).offset as c_long +pub extern "C" fn telldir(dir: &mut DIR) -> c_long { + dir.opaque_offset as c_long } #[no_mangle] -pub unsafe extern "C" fn seekdir(dir: *mut DIR, off: c_long) { - let _ = (*dir).file.seek(SeekFrom::Start(off as u64)); - (*dir).offset = off as usize; - (*dir).index = 0; - (*dir).len = 0; +pub extern "C" fn seekdir(dir: &mut DIR, off: c_long) { + dir.seek( + off.try_into() + .expect("off must come from telldir, thus never negative"), + ); } #[no_mangle] -pub unsafe extern "C" fn rewinddir(dir: *mut DIR) { - seekdir(dir, 0) +pub extern "C" fn rewinddir(dir: &mut DIR) { + dir.rewind(); } #[no_mangle] @@ -149,7 +216,7 @@ pub unsafe extern "C" fn scandir( platform::ERRNO.set(0); loop { - let entry: *mut dirent = readdir(dir); + let entry: *mut dirent = readdir(&mut *dir); if entry.is_null() { break; } @@ -170,7 +237,7 @@ pub unsafe extern "C" fn scandir( } } - closedir(dir); + closedir(Box::from_raw(dir)); let len = vec.len(); if let Err(_) = vec.shrink_to_fit() { diff --git a/src/header/fcntl/cbindgen.toml b/src/header/fcntl/cbindgen.toml index 40377c713a..012de05c0c 100644 --- a/src/header/fcntl/cbindgen.toml +++ b/src/header/fcntl/cbindgen.toml @@ -6,9 +6,5 @@ style = "Tag" no_includes = true cpp_compat = true -[defines] -"target_os=linux" = "__linux__" -"target_os=redox" = "__redox__" - [enum] prefix_with_name = true diff --git a/src/header/signal/cbindgen.toml b/src/header/signal/cbindgen.toml index 3c79a66073..f29e6aa396 100644 --- a/src/header/signal/cbindgen.toml +++ b/src/header/signal/cbindgen.toml @@ -6,10 +6,6 @@ style = "Tag" no_includes = true cpp_compat = true -[defines] -"target_os=linux" = "__linux__" -"target_os=redox" = "__redox__" - [enum] prefix_with_name = true diff --git a/src/header/stdlib/cbindgen.toml b/src/header/stdlib/cbindgen.toml index fb6dde8a17..6de6a6fa4b 100644 --- a/src/header/stdlib/cbindgen.toml +++ b/src/header/stdlib/cbindgen.toml @@ -6,9 +6,6 @@ style = "Type" no_includes = true cpp_compat = true -[defines] -"target_pointer_width=64" = "__LP64__" - [enum] prefix_with_name = true diff --git a/src/header/sys_epoll/cbindgen.toml b/src/header/sys_epoll/cbindgen.toml index af85975df7..64ce6c75b7 100644 --- a/src/header/sys_epoll/cbindgen.toml +++ b/src/header/sys_epoll/cbindgen.toml @@ -5,10 +5,5 @@ style = "Tag" no_includes = true cpp_compat = true -[defines] -"target_os=linux" = "__linux__" -"target_os=redox" = "__redox__" -"target_pointer_width=64" = "__LP64__" - [enum] prefix_with_name = true diff --git a/src/header/sys_ioctl/cbindgen.toml b/src/header/sys_ioctl/cbindgen.toml index fa59f7887a..d7882bc0e6 100644 --- a/src/header/sys_ioctl/cbindgen.toml +++ b/src/header/sys_ioctl/cbindgen.toml @@ -4,10 +4,6 @@ style = "Tag" no_includes = true cpp_compat = true -[defines] -"target_os=linux" = "__linux__" -"target_os=redox" = "__redox__" - [enum] prefix_with_name = true diff --git a/src/header/sys_mman/cbindgen.toml b/src/header/sys_mman/cbindgen.toml index eb433afb6a..6846db04d8 100644 --- a/src/header/sys_mman/cbindgen.toml +++ b/src/header/sys_mman/cbindgen.toml @@ -6,9 +6,5 @@ style = "Tag" no_includes = true cpp_compat = true -[defines] -"target_os=linux" = "__linux__" -"target_os=redox" = "__redox__" - [enum] prefix_with_name = true diff --git a/src/header/sys_resource/cbindgen.toml b/src/header/sys_resource/cbindgen.toml index 40aed298b7..33c1b7b4db 100644 --- a/src/header/sys_resource/cbindgen.toml +++ b/src/header/sys_resource/cbindgen.toml @@ -6,10 +6,6 @@ style = "Tag" no_includes = true cpp_compat = true -[defines] -"target_os=linux" = "__linux__" -"target_os=redox" = "__redox__" - [enum] prefix_with_name = true diff --git a/src/header/sys_socket/cbindgen.toml b/src/header/sys_socket/cbindgen.toml index e39a7856ce..30faf33a14 100644 --- a/src/header/sys_socket/cbindgen.toml +++ b/src/header/sys_socket/cbindgen.toml @@ -6,10 +6,6 @@ style = "Tag" no_includes = true cpp_compat = true -[defines] -"target_os=linux" = "__linux__" -"target_os=redox" = "__redox__" - [enum] prefix_with_name = true diff --git a/src/header/termios/cbindgen.toml b/src/header/termios/cbindgen.toml index 71b24793a3..c5b90ae823 100644 --- a/src/header/termios/cbindgen.toml +++ b/src/header/termios/cbindgen.toml @@ -5,10 +5,6 @@ style = "Tag" no_includes = true cpp_compat = true -[defines] -"target_os=linux" = "__linux__" -"target_os=redox" = "__redox__" - [enum] prefix_with_name = true diff --git a/src/header/time/cbindgen.toml b/src/header/time/cbindgen.toml index 0b7e7399bd..3a77d78d29 100644 --- a/src/header/time/cbindgen.toml +++ b/src/header/time/cbindgen.toml @@ -7,7 +7,3 @@ cpp_compat = true [enum] prefix_with_name = true - -[defines] -"target_os = linux" = "__linux__" -"target_os = redox" = "__redox__" diff --git a/src/header/unistd/cbindgen.toml b/src/header/unistd/cbindgen.toml index 610d6d21fe..30215e1656 100644 --- a/src/header/unistd/cbindgen.toml +++ b/src/header/unistd/cbindgen.toml @@ -8,7 +8,3 @@ cpp_compat = true [enum] prefix_with_name = true - -[defines] -"target_os = linux" = "__linux__" -"target_os = redox" = "__redox__" diff --git a/src/lib.rs b/src/lib.rs index 1baebcaf2c..2104fbc851 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(lang_items)] +#![feature(let_chains)] #![feature(linkage)] #![feature(stmt_expr_attributes)] #![feature(str_internals)] diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 5d469d6aea..66f222b32a 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -1,5 +1,5 @@ use crate::io::Write; -use core::{arch::asm, ptr}; +use core::{arch::asm, mem::offset_of, ptr}; use super::{types::*, Pal, ERRNO}; use crate::{ @@ -12,6 +12,7 @@ use crate::header::{ sys_stat::stat, sys_statvfs::statvfs, sys_time::{timeval, timezone}, + unistd::SEEK_SET, }; // use header::sys_times::tms; use crate::{ @@ -281,8 +282,16 @@ impl Pal for Sys { } } - fn getdents(fd: c_int, dirents: *mut dirent, bytes: usize) -> c_int { - unsafe { syscall!(GETDENTS64, fd, dirents, bytes) as c_int } + fn getdents(fd: c_int, buf: &mut [u8], _off: u64) -> Result { + e_raw(unsafe { syscall!(GETDENTS64, fd, buf.as_mut_ptr(), buf.len()) }) + } + fn dir_seek(fd: c_int, off: u64) -> Result<(), Errno> { + e_raw(unsafe { syscall!(LSEEK, fd, off, SEEK_SET) })?; + Ok(()) + } + unsafe fn dent_reclen_offset(this_dent: &[u8], offset: usize) -> Option<(u16, u64)> { + let dent = this_dent.as_ptr().cast::(); + Some(((*dent).d_reclen, (*dent).d_off as u64)) } fn getegid() -> gid_t { diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index b20790a04c..96e97df695 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -93,7 +93,13 @@ pub trait Pal { fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char; - fn getdents(fd: c_int, dirents: *mut dirent, bytes: usize) -> c_int; + fn getdents(fd: c_int, buf: &mut [u8], opaque_offset: u64) -> Result; + fn dir_seek(fd: c_int, opaque_offset: u64) -> Result<(), Errno>; + + // SAFETY: This_dent must satisfy platform-specific size and alignment constraints. On Linux, + // this means the buffer came from a valid getdents64 invocation, whereas on Redox, every + // possible this_dent slice is safe (and will be validated). + unsafe fn dent_reclen_offset(this_dent: &[u8], offset: usize) -> Option<(u16, u64)>; fn getegid() -> gid_t; diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 0a75f7c454..96154409b2 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -1,8 +1,13 @@ -use core::{convert::TryFrom, mem, ptr, slice, str}; +use core::{ + convert::TryFrom, + mem::{self, size_of}, + ptr, slice, str, +}; use redox_rt::RtTcb; use syscall::{ self, data::{Map, Stat as redox_stat, StatVfs as redox_statvfs, TimeSpec as redox_timespec}, + dirent::{DirentHeader, DirentKind}, Error, PtraceEvent, Result, EMFILE, }; @@ -12,7 +17,10 @@ use crate::{ fs::File, header::{ dirent::dirent, - errno::{EBADF, EBADFD, EBADR, EINVAL, EIO, ENOENT, ENOMEM, ENOSYS, EPERM, ERANGE}, + errno::{ + EBADF, EBADFD, EBADR, EINVAL, EIO, ENAMETOOLONG, ENOENT, ENOMEM, ENOSYS, EOPNOTSUPP, + EPERM, ERANGE, + }, fcntl, limits, sys_mman::{MAP_ANONYMOUS, MAP_FAILED, PROT_READ, PROT_WRITE}, sys_random, @@ -353,97 +361,74 @@ impl Pal for Sys { buf } - fn getdents(fd: c_int, mut dirents: *mut dirent, max_bytes: usize) -> c_int { - //TODO: rewrite this code. Originally the *dirents = dirent { ... } stuff below caused - // massive issues. This has been hacked around, but it still isn't perfect + fn getdents(fd: c_int, buf: &mut [u8], opaque: u64) -> Result { + //println!("GETDENTS {} into ({:p}+{})", fd, buf.as_ptr(), buf.len()); - // Get initial reading position - let mut read = match syscall::lseek(fd as usize, 0, syscall::SEEK_CUR) { - Ok(pos) => pos as isize, - Err(err) => return -err.errno, - }; + const HEADER_SIZE: usize = size_of::(); - let mut written = 0; - let mut buf = [0; 1024]; - - let mut name = [0; 256]; - let mut i = 0; - - let mut flush = |written: &mut usize, i: &mut usize, name: &mut [c_char; 256]| { - if *i < name.len() { - // Set NUL byte - name[*i] = 0; - } - // Get size: full size - unused bytes - if *written + mem::size_of::() > max_bytes { - // Seek back to after last read entry and return - match syscall::lseek(fd as usize, read, syscall::SEEK_SET) { - Ok(_) => return Some(*written as c_int), - Err(err) => return Some(-err.errno), - } - } - let size = mem::size_of::() - name.len().saturating_sub(*i + 1); - unsafe { - //This is the offending code mentioned above - *dirents = dirent { - d_ino: 0, - d_off: read as off_t, - d_reclen: size as c_ushort, - d_type: 0, - d_name: *name, - }; - dirents = (dirents as *mut u8).offset(size as isize) as *mut dirent; - } - read += *i as isize + /* newline */ 1; - *written += size; - *i = 0; - None - }; - - loop { - // Read a chunk from the directory - let len = match syscall::read(fd as usize, &mut buf) { - Ok(0) => { - if i > 0 { - if let Some(value) = flush(&mut written, &mut i, &mut name) { - return value; - } - } - return written as c_int; - } - Ok(n) => n, - Err(err) => return -err.errno, - }; - - // Handle everything - let mut start = 0; - while start < len { - let buf = &buf[start..len]; - - // Copy everything up until a newline - let newline = buf.iter().position(|&c| c == b'\n'); - let pre_len = newline.unwrap_or(buf.len()); - let post_len = newline.map(|i| i + 1).unwrap_or(buf.len()); - if i < pre_len { - // Reserve space for NUL byte - let name_len = name.len() - 1; - let name = &mut name[i..name_len]; - let copy = pre_len.min(name.len()); - let buf = unsafe { slice::from_raw_parts(buf.as_ptr() as *const c_char, copy) }; - name[..copy].copy_from_slice(buf); - } - - i += pre_len; - start += post_len; - - // Write the directory entry - if newline.is_some() { - if let Some(value) = flush(&mut written, &mut i, &mut name) { - return value; - } - } + // Use syscall if it exists. + match unsafe { + syscall::syscall5( + syscall::SYS_GETDENTS, + fd as usize, + buf.as_mut_ptr() as usize, + buf.len(), + HEADER_SIZE, + opaque as usize, + ) + } { + Err(Error { + errno: EOPNOTSUPP | ENOSYS, + }) => (), + other => { + //println!("REAL GETDENTS {:?}", other); + return Ok(other?); } } + + // Otherwise, for legacy schemes, assume the buffer is pre-arranged (all schemes do this in + // practice), and just read the name. If multiple names appear, pretend it didn't happen + // and just use the first entry. + + let (header, name) = buf.split_at_mut(size_of::()); + + let bytes_read = Sys::pread(fd, name, opaque as i64)? as usize; + if bytes_read == 0 { + return Ok(0); + } + + let (name_len, advance) = match name[..bytes_read].iter().position(|c| *c == b'\n') { + Some(idx) => (idx, idx + 1), + + // Insufficient space for NUL byte, or entire entry was not read. Indicate we need a + // larger buffer. + None if bytes_read == name.len() => return Err(Errno(EINVAL)), + + None => (bytes_read, name.len()), + }; + name[name_len] = b'\0'; + + let record_len = u16::try_from(size_of::() + name_len + 1) + .map_err(|_| Error::new(ENAMETOOLONG))?; + header.copy_from_slice(&DirentHeader { + inode: 0, + next_opaque_id: opaque + advance as u64, + record_len, + kind: DirentKind::Unspecified as u8, + }); + //println!("EMULATED GETDENTS"); + + Ok(record_len.into()) + } + fn dir_seek(_fd: c_int, _off: u64) -> Result<(), Errno> { + // Redox getdents takes an explicit (opaque) offset, so this is a no-op. + Ok(()) + } + // NOTE: fn is unsafe, but this just means we can assume more things. impl is safe + unsafe fn dent_reclen_offset(this_dent: &[u8], offset: usize) -> Option<(u16, u64)> { + let mut header = DirentHeader::default(); + header.copy_from_slice(&this_dent.get(..size_of::())?); + Some((header.record_len, header.next_opaque_id)) } fn getegid() -> gid_t { diff --git a/tests/dirent/main.c b/tests/dirent/main.c index f1c92ed506..b524ed1e98 100644 --- a/tests/dirent/main.c +++ b/tests/dirent/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -26,6 +27,7 @@ int main(void) { puts("--- Testing rewind ---"); rewinddir(dir); entry = readdir(dir); + assert(entry != NULL); puts(entry->d_name); // puts("--- Testing seek ---");