|
@@ -0,0 +1,124 @@
|
|
|
+#![no_std]
|
|
|
+#![feature(alloc)]
|
|
|
+///! dirent implementation following http://pubs.opengroup.org/onlinepubs/009695399/basedefs/dirent.h.html
|
|
|
+
|
|
|
+extern crate alloc;
|
|
|
+extern crate errno;
|
|
|
+extern crate fcntl;
|
|
|
+extern crate platform;
|
|
|
+extern crate stdio;
|
|
|
+extern crate unistd;
|
|
|
+
|
|
|
+use alloc::boxed::Box;
|
|
|
+use core::{mem, ptr};
|
|
|
+use platform::types::*;
|
|
|
+
|
|
|
+// This is here because cbindgen doesn't understand size_of calls.
|
|
|
+// We set this constant inside C too, from bits/dirent.h
|
|
|
+const _DIRENT_SIZE: usize = mem::size_of::<dirent>();
|
|
|
+
|
|
|
+const _DIR_BUF_SIZE: usize = _DIRENT_SIZE * 3;
|
|
|
+
|
|
|
+// No repr(C) needed, C won't see the content
|
|
|
+// TODO: ***THREAD SAFETY***
|
|
|
+pub struct DIR {
|
|
|
+ fd: c_int,
|
|
|
+ buf: [c_char; _DIR_BUF_SIZE],
|
|
|
+ // index & len are specified in bytes
|
|
|
+ index: usize,
|
|
|
+ len: usize,
|
|
|
+
|
|
|
+ // Offset is like the total index, never erased It is used as an
|
|
|
+ // alternative to dirent's d_off, but works on redox too.
|
|
|
+ offset: usize
|
|
|
+}
|
|
|
+
|
|
|
+#[repr(C)]
|
|
|
+pub struct dirent {
|
|
|
+ pub d_ino: ino_t,
|
|
|
+ pub d_off: off_t,
|
|
|
+ pub d_reclen: c_ushort,
|
|
|
+ pub d_type: c_uchar,
|
|
|
+ pub d_name: [c_char; 256]
|
|
|
+}
|
|
|
+
|
|
|
+#[no_mangle]
|
|
|
+pub extern "C" fn opendir(path: *const c_char) -> *mut DIR {
|
|
|
+ let fd = platform::open(path, fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC, 0o755);
|
|
|
+
|
|
|
+ if fd < 0 {
|
|
|
+ return ptr::null_mut();
|
|
|
+ }
|
|
|
+
|
|
|
+ Box::into_raw(Box::new(DIR {
|
|
|
+ fd,
|
|
|
+ buf: [0; _DIR_BUF_SIZE],
|
|
|
+ index: 0,
|
|
|
+ len: 0,
|
|
|
+ offset: 0
|
|
|
+ }))
|
|
|
+}
|
|
|
+
|
|
|
+#[no_mangle]
|
|
|
+pub unsafe extern "C" fn closedir(dir: *mut DIR) -> c_int {
|
|
|
+ let ret = platform::close((*dir).fd);
|
|
|
+ Box::from_raw(dir);
|
|
|
+ ret
|
|
|
+}
|
|
|
+
|
|
|
+#[no_mangle]
|
|
|
+pub unsafe extern "C" fn readdir(dir: *mut DIR) -> *mut dirent {
|
|
|
+ if (*dir).index >= (*dir).len {
|
|
|
+ let read = platform::getdents(
|
|
|
+ (*dir).fd,
|
|
|
+ (*dir).buf.as_mut_ptr() as *mut platform::types::dirent,
|
|
|
+ (*dir).buf.len()
|
|
|
+ );
|
|
|
+ if read <= 0 {
|
|
|
+ if read != 0 && read != -errno::ENOENT {
|
|
|
+ platform::errno = -read;
|
|
|
+ }
|
|
|
+ return ptr::null_mut();
|
|
|
+ }
|
|
|
+
|
|
|
+ (*dir).index = 0;
|
|
|
+ (*dir).len = read as usize;
|
|
|
+ }
|
|
|
+
|
|
|
+ let ptr = (*dir).buf.as_mut_ptr().offset((*dir).index as isize) as *mut dirent;
|
|
|
+
|
|
|
+ #[cfg(target_os = "redox")] {
|
|
|
+ if (*dir).index != 0 || (*dir).offset != 0 {
|
|
|
+ // This should happen every time but the first, making the offset
|
|
|
+ // point to the current element and not the next
|
|
|
+ (*dir).offset += _DIRENT_SIZE;
|
|
|
+ }
|
|
|
+ (*ptr).d_off = (*dir).offset as off_t;
|
|
|
+ }
|
|
|
+ #[cfg(not(target_os = "redox"))] {
|
|
|
+ (*dir).offset = (*ptr).d_off as usize;
|
|
|
+ }
|
|
|
+
|
|
|
+ (*dir).index += (*ptr).d_reclen as usize;
|
|
|
+ ptr
|
|
|
+}
|
|
|
+// #[no_mangle]
|
|
|
+pub extern "C" fn readdir_r(_dir: *mut DIR, _entry: *mut dirent, _result: *mut *mut dirent) -> *mut dirent {
|
|
|
+ unimplemented!(); // plus, deprecated
|
|
|
+}
|
|
|
+
|
|
|
+#[no_mangle]
|
|
|
+pub unsafe extern "C" fn telldir(dir: *mut DIR) -> c_long {
|
|
|
+ (*dir).offset as c_long
|
|
|
+}
|
|
|
+#[no_mangle]
|
|
|
+pub unsafe extern "C" fn seekdir(dir: *mut DIR, off: c_long) {
|
|
|
+ unistd::lseek((*dir).fd, off, unistd::SEEK_SET);
|
|
|
+ (*dir).offset = off as usize;
|
|
|
+ (*dir).index = 0;
|
|
|
+ (*dir).len = 0;
|
|
|
+}
|
|
|
+#[no_mangle]
|
|
|
+pub unsafe extern "C" fn rewinddir(dir: *mut DIR) {
|
|
|
+ seekdir(dir, 0)
|
|
|
+}
|