Browse Source

Implement dirent.h

jD91mZM2 6 years ago
parent
commit
83949290c9

+ 13 - 0
Cargo.lock

@@ -91,6 +91,18 @@ dependencies = [
  "platform 0.1.0",
 ]
 
+[[package]]
+name = "dirent"
+version = "0.1.0"
+dependencies = [
+ "cbindgen 0.5.2",
+ "errno 0.1.0",
+ "fcntl 0.1.0",
+ "platform 0.1.0",
+ "stdio 0.1.0",
+ "unistd 0.1.0",
+]
+
 [[package]]
 name = "dtoa"
 version = "0.4.3"
@@ -302,6 +314,7 @@ dependencies = [
  "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins.git)",
  "ctype 0.1.0",
+ "dirent 0.1.0",
  "errno 0.1.0",
  "fcntl 0.1.0",
  "fenv 0.1.0",

+ 1 - 0
Cargo.toml

@@ -16,6 +16,7 @@ cc = "1.0.17"
 [dependencies]
 arpainet = { path = "src/arpainet" }
 ctype = { path = "src/ctype" }
+dirent = { path = "src/dirent" }
 errno = { path = "src/errno" }
 fcntl = { path = "src/fcntl" }
 fenv = { path = "src/fenv" }

+ 6 - 0
include/bits/dirent.h

@@ -0,0 +1,6 @@
+#ifndef _BITS_DIRENT_H
+#define _BITS_DIRENT_H
+
+#define _DIRENT_SIZE (sizeof dirent)
+
+#endif /* _BITS_DIRENT_H */

+ 15 - 0
src/dirent/Cargo.toml

@@ -0,0 +1,15 @@
+[package]
+name = "dirent"
+version = "0.1.0"
+authors = ["jD91mZM2 <me@krake.one>"]
+build = "build.rs"
+
+[build-dependencies]
+cbindgen = { path = "../../cbindgen" }
+
+[dependencies]
+errno = { path = "../errno" }
+fcntl = { path = "../fcntl" }
+platform = { path = "../platform" }
+stdio = { path = "../stdio" }
+unistd = { path = "../unistd" }

+ 11 - 0
src/dirent/build.rs

@@ -0,0 +1,11 @@
+extern crate cbindgen;
+
+use std::{env, fs};
+
+fn main() {
+    let crate_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
+    fs::create_dir_all("../../target/include").expect("failed to create include directory");
+    cbindgen::generate(crate_dir)
+        .expect("failed to generate bindings")
+        .write_to_file("../../target/include/dirent.h");
+}

+ 8 - 0
src/dirent/cbindgen.toml

@@ -0,0 +1,8 @@
+sys_includes = ["sys/types.h"]
+include_guard = "_DIRENT_H"
+language = "C"
+style = "Both"
+trailer = "#include <bits/dirent.h>"
+
+[enum]
+prefix_with_name = true

+ 124 - 0
src/dirent/src/lib.rs

@@ -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)
+}

+ 1 - 0
src/fcntl/src/linux.rs

@@ -8,4 +8,5 @@ pub const O_TRUNC: c_int = 0x0200;
 pub const O_ACCMODE: c_int = O_RDONLY | O_WRONLY | O_RDWR;
 pub const O_APPEND: c_int = 0o2000;
 pub const O_CLOEXEC: c_int = 0o2_000_000;
+pub const O_DIRECTORY: c_int = 0o200_000;
 pub const O_EXCL: c_int = 0o200;

+ 1 - 0
src/lib.rs

@@ -8,6 +8,7 @@ extern crate platform;
 
 pub extern crate arpainet;
 pub extern crate ctype;
+pub extern crate dirent;
 pub extern crate errno;
 pub extern crate fcntl;
 pub extern crate fenv;

+ 4 - 0
src/platform/src/linux/mod.rs

@@ -111,6 +111,10 @@ pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char {
     }
 }
 
+pub fn getdents(fd: c_int, dirents: *mut dirent, bytes: usize) -> c_int {
+    unsafe { syscall!(GETDENTS64, fd, dirents, bytes) as c_int }
+}
+
 pub fn getegid() -> gid_t {
     e(unsafe { syscall!(GETEGID) })
 }

+ 52 - 4
src/platform/src/redox/mod.rs

@@ -163,12 +163,12 @@ pub unsafe extern "C" fn execve(
                         // If the environment variable has no value, there
                         // is no need to write anything to the env scheme.
                         if sep + 1 < slice.len() {
-                            let n = match syscall::write(fd, &slice[sep + 1..]) {
-                                Ok(n) => n,
+                            match syscall::write(fd, &slice[sep + 1..]) {
+                                Ok(_) => (),
                                 err => {
                                     return e(err) as c_int;
                                 }
-                            };
+                            }
                         }
                         // Cleanup after adding the variable.
                         match syscall::close(fd) {
@@ -274,6 +274,54 @@ pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char {
     }
 }
 
+pub fn getdents(fd: c_int, mut dirents: *mut dirent, mut bytes: usize) -> c_int {
+    let mut amount = 0;
+
+    let mut buf = [0; 1024];
+    let mut bindex = 0;
+    let mut blen = 0;
+
+    let mut name = [0; 256];
+    let mut nindex = 0;
+
+    loop {
+        if bindex >= blen {
+            bindex = 0;
+            blen = match syscall::read(fd as usize, &mut buf) {
+                Ok(0) => return amount,
+                Ok(n) => n,
+                Err(err) => return -err.errno
+            };
+        }
+
+        if buf[bindex] == b'\n' {
+            // Put a NUL byte either at the end, or if it's too big, at where it's truncated.
+            name[nindex.min(name.len() - 1)] = 0;
+            unsafe {
+                *dirents = dirent {
+                    d_ino: 0,
+                    d_off: 0,
+                    d_reclen: mem::size_of::<dirent>() as c_ushort,
+                    d_type: 0,
+                    d_name: name
+                };
+                dirents = dirents.offset(1);
+            }
+            amount += 1;
+            if bytes <= mem::size_of::<dirent>() {
+                return amount;
+            }
+            bytes -= mem::size_of::<dirent>();
+        } else {
+            if nindex < name.len() {
+                name[nindex] = buf[bindex] as c_char;
+            }
+            nindex += 1;
+            bindex += 1;
+        }
+    }
+}
+
 pub fn getegid() -> gid_t {
     e(syscall::getegid()) as gid_t
 }
@@ -401,7 +449,7 @@ pub fn link(path1: *const c_char, path2: *const c_char) -> c_int {
     e(unsafe { syscall::link(path1.as_ptr(), path2.as_ptr()) }) as c_int
 }
 
-pub fn listen(socket: c_int, backlog: c_int) -> c_int {
+pub fn listen(_socket: c_int, _backlog: c_int) -> c_int {
     // TODO
     0
 }

+ 10 - 1
src/platform/src/types.rs

@@ -107,7 +107,7 @@ pub struct stat {
     // Compared to glibc, our struct is for some reason 48 bytes too small.
     // Accessing atime works, so clearly the struct isn't incorrect...
     // This works.
-    pub _pad: [u8; 48]
+    pub _pad: [c_char; 48]
 }
 
 pub const AF_INET: c_int = 2;
@@ -167,3 +167,12 @@ pub struct utsname {
     pub machine: [c_char; UTSLENGTH],
     pub domainname: [c_char; UTSLENGTH],
 }
+
+#[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]
+}

+ 1 - 1
src/sys_stat/src/lib.rs

@@ -52,7 +52,7 @@ pub struct stat {
     // Compared to glibc, our struct is for some reason 48 bytes too small.
     // Accessing atime works, so clearly the struct isn't incorrect...
     // This works.
-    pub _pad: [u8; 48]
+    pub _pad: [c_char; 48]
 }
 
 #[no_mangle]

+ 2 - 1
tests/.gitignore

@@ -56,13 +56,13 @@ unistd/getopt
 unistd/pipe
 unistd/rmdir
 unistd/sleep
-unistd/stat
 unistd/write
 waitpid
 wchar/mbrtowc
 wchar/mbsrtowcs
 wchar/putwchar
 wchar/wcrtomb
+dirent
 stdlib/alloc
 stdlib/bsearch
 stdlib/mktemp
@@ -71,3 +71,4 @@ unistd/gethostname
 unistd/getid
 unistd/link
 unistd/setid
+unistd/stat

+ 1 - 0
tests/Makefile

@@ -65,6 +65,7 @@ EXPECT_BINS=\
 # Binaries that may generate varied output
 BINS=\
 	$(EXPECT_BINS) \
+	dirent \
 	stdlib/alloc \
 	stdlib/bsearch \
 	stdlib/mktemp \

+ 38 - 0
tests/dirent.c

@@ -0,0 +1,38 @@
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+
+int main() {
+    printf("%lu\n", sizeof(struct dirent));
+
+    DIR* dir = opendir("example_dir/");
+
+    if (dir == NULL) {
+        perror("opendir");
+        return 1;
+    }
+
+    struct dirent* entry;
+
+    int tell = 0;
+
+    for (char counter = 0; (entry = readdir(dir)); counter += 1) {
+        puts(entry->d_name);
+
+        if (counter == 4) {
+            tell = telldir(dir);
+        }
+    }
+
+    puts("--- Testing rewind ---");
+    rewinddir(dir);
+    entry = readdir(dir);
+    puts(entry->d_name);
+
+    puts("--- Testing seek ---");
+    // Why this doesn't cause it to actually go to the 4th element is beyond
+    // me, but glibc acts the same way.
+    seekdir(dir, tell);
+    entry = readdir(dir);
+    puts(entry->d_name);
+}

+ 0 - 0
tests/example_dir/1-never-gonna-give-you-up


+ 0 - 0
tests/example_dir/2-never-gonna-let-you-down


+ 0 - 0
tests/example_dir/3-never-gonna-run-around


+ 0 - 0
tests/example_dir/4-and-desert-you


+ 0 - 0
tests/example_dir/5-never-gonna-make-you-cry


+ 0 - 0
tests/example_dir/6-never-gonna-say-goodbye


+ 0 - 0
tests/example_dir/7-never-gonna-tell-a-lie


+ 0 - 0
tests/example_dir/8-and-hurt-you