From 758f681590831290351ee8345c4b795fadfed733 Mon Sep 17 00:00:00 2001 From: jD91mZM2 <me@krake.one> Date: Sun, 7 Oct 2018 14:43:54 +0200 Subject: [PATCH] Implement scandir --- src/header/dirent/mod.rs | 110 ++++++++++++++++++++++----- tests/Makefile | 3 +- tests/{dirent.c => dirent/main.c} | 0 tests/dirent/scandir.c | 23 ++++++ tests/expected/dirent/scandir.stderr | 0 tests/expected/dirent/scandir.stdout | 9 +++ 6 files changed, 126 insertions(+), 19 deletions(-) rename tests/{dirent.c => dirent/main.c} (100%) create mode 100644 tests/dirent/scandir.c create mode 100644 tests/expected/dirent/scandir.stderr create mode 100644 tests/expected/dirent/scandir.stdout diff --git a/src/header/dirent/mod.rs b/src/header/dirent/mod.rs index 7ea6e363..b2a133bd 100644 --- a/src/header/dirent/mod.rs +++ b/src/header/dirent/mod.rs @@ -4,19 +4,20 @@ use alloc::boxed::Box; use core::{mem, ptr}; use c_str::CStr; -use header::{errno, fcntl, unistd}; -use platform; +use fs::File; +use header::{errno, fcntl, stdlib, string}; +use io::{Seek, SeekFrom}; use platform::types::*; use platform::{Pal, Sys}; +use platform; const DIR_BUF_SIZE: usize = mem::size_of::<dirent>() * 3; // No repr(C) needed, C won't see the content -// TODO: ***THREAD SAFETY*** pub struct DIR { - fd: c_int, + file: File, buf: [c_char; DIR_BUF_SIZE], - // index & len are specified in bytes + // index and len are specified in bytes index: usize, len: usize, @@ -26,6 +27,7 @@ pub struct DIR { } #[repr(C)] +#[derive(Clone)] pub struct dirent { pub d_ino: ino_t, pub d_off: off_t, @@ -37,18 +39,16 @@ pub struct dirent { #[no_mangle] pub extern "C" fn opendir(path: *const c_char) -> *mut DIR { let path = unsafe { CStr::from_ptr(path) }; - let fd = Sys::open( + let file = match File::open( path, - fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC, - 0, - ); - - if fd < 0 { - return ptr::null_mut(); - } + fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC + ) { + Ok(file) => file, + Err(_) => return ptr::null_mut() + }; Box::into_raw(Box::new(DIR { - fd, + file, buf: [0; DIR_BUF_SIZE], index: 0, len: 0, @@ -58,8 +58,13 @@ pub extern "C" fn opendir(path: *const c_char) -> *mut DIR { #[no_mangle] pub unsafe extern "C" fn closedir(dir: *mut DIR) -> c_int { - let ret = Sys::close((*dir).fd); - Box::from_raw(dir); + 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 } @@ -67,7 +72,7 @@ pub unsafe extern "C" fn closedir(dir: *mut DIR) -> c_int { pub unsafe extern "C" fn readdir(dir: *mut DIR) -> *mut dirent { if (*dir).index >= (*dir).len { let read = Sys::getdents( - (*dir).fd, + *(*dir).file, (*dir).buf.as_mut_ptr() as *mut dirent, (*dir).buf.len(), ); @@ -116,7 +121,7 @@ pub unsafe extern "C" fn telldir(dir: *mut DIR) -> c_long { } #[no_mangle] pub unsafe extern "C" fn seekdir(dir: *mut DIR, off: c_long) { - unistd::lseek((*dir).fd, off, unistd::SEEK_SET); + let _ = (*dir).file.seek(SeekFrom::Start(off as u64)); (*dir).offset = off as usize; (*dir).index = 0; (*dir).len = 0; @@ -125,3 +130,72 @@ pub unsafe extern "C" fn seekdir(dir: *mut DIR, off: c_long) { pub unsafe extern "C" fn rewinddir(dir: *mut DIR) { seekdir(dir, 0) } + +#[no_mangle] +pub unsafe extern "C" fn alphasort( + first: *mut *const dirent, + second: *mut *const dirent +) -> c_int { + string::strcoll((**first).d_name.as_ptr(), (**second).d_name.as_ptr()) +} + +#[no_mangle] +pub unsafe extern "C" fn scandir( + dirp: *const c_char, + namelist: *mut *mut *mut dirent, + filter: Option<extern "C" fn(_: *const dirent) -> c_int>, + compare: Option<extern "C" fn(_: *mut *const dirent, _: *mut *const dirent) -> c_int> +) -> c_int { + let dir = opendir(dirp); + if dir.is_null() { + return -1; + } + + let old_errno = platform::errno; + + let mut len: isize = 0; + let mut cap: isize = 4; + *namelist = platform::alloc(cap as usize * mem::size_of::<*mut dirent>()) as *mut *mut dirent; + + loop { + platform::errno = 0; + let entry = readdir(dir); + if entry.is_null() { + break; + } + + if let Some(filter) = filter { + if filter(entry) == 0 { + continue; + } + } + + if len >= cap { + cap *= 2; + *namelist = platform::realloc( + *namelist as *mut c_void, + cap as usize * mem::size_of::<*mut dirent>() + ) as *mut *mut dirent; + } + + let copy = platform::alloc(mem::size_of::<dirent>()) as *mut dirent; + *copy = (*entry).clone(); + *(*namelist).offset(len) = copy; + len += 1; + } + + closedir(dir); + + if platform::errno != 0 { + while len > 0 { + len -= 1; + platform::free(*(*namelist).offset(len) as *mut c_void); + } + platform::free(*namelist as *mut c_void); + -1 + } else { + platform::errno = old_errno; + stdlib::qsort(*namelist as *mut c_void, len as size_t, mem::size_of::<*mut dirent>(), mem::transmute(compare)); + len as c_int + } +} diff --git a/tests/Makefile b/tests/Makefile index 71ee5e05..a7bdb63a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -4,6 +4,7 @@ EXPECT_BINS=\ arpainet \ assert \ ctype \ + dirent/scandir \ error \ fcntl/create \ fcntl/fcntl \ @@ -80,7 +81,7 @@ EXPECT_BINS=\ # Binaries that may generate varied output BINS=\ $(EXPECT_BINS) \ - dirent \ + dirent/main \ pwd \ stdlib/alloc \ stdlib/bsearch \ diff --git a/tests/dirent.c b/tests/dirent/main.c similarity index 100% rename from tests/dirent.c rename to tests/dirent/main.c diff --git a/tests/dirent/scandir.c b/tests/dirent/scandir.c new file mode 100644 index 00000000..383a6ca0 --- /dev/null +++ b/tests/dirent/scandir.c @@ -0,0 +1,23 @@ +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int filter(const struct dirent* dirent) { + return strstr(dirent->d_name, "3") == NULL; +} + +int main() { + struct dirent** array; + int len = scandir("example_dir/", &array, filter, alphasort); + if (len < 0) { + perror("scandir"); + return -1; + } + + for(int i = 0; i < len; i += 1) { + puts(array[i]->d_name); + free(array[i]); + } + free(array); +} diff --git a/tests/expected/dirent/scandir.stderr b/tests/expected/dirent/scandir.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/expected/dirent/scandir.stdout b/tests/expected/dirent/scandir.stdout new file mode 100644 index 00000000..c60de319 --- /dev/null +++ b/tests/expected/dirent/scandir.stdout @@ -0,0 +1,9 @@ +. +.. +1-never-gonna-give-you-up +2-never-gonna-let-you-down +4-and-desert-you +5-never-gonna-make-you-cry +6-never-gonna-say-goodbye +7-never-gonna-tell-a-lie +8-and-hurt-you -- GitLab