Backwards-compatibly rewrite getdents to use special syscall.
This commit is contained in:
+136
-69
@@ -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::<dirent>() * 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<u8>,
|
||||
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<Box<Self>, 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::<DirentHeader>() {
|
||||
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<DIR>) -> 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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@ style = "Type"
|
||||
no_includes = true
|
||||
cpp_compat = true
|
||||
|
||||
[defines]
|
||||
"target_pointer_width=64" = "__LP64__"
|
||||
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -7,7 +7,3 @@ cpp_compat = true
|
||||
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
|
||||
[defines]
|
||||
"target_os = linux" = "__linux__"
|
||||
"target_os = redox" = "__redox__"
|
||||
|
||||
@@ -8,7 +8,3 @@ cpp_compat = true
|
||||
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
|
||||
[defines]
|
||||
"target_os = linux" = "__linux__"
|
||||
"target_os = redox" = "__redox__"
|
||||
|
||||
Reference in New Issue
Block a user