From 571b4d49765b22dd763a0ea5dc06ebf32f67db91 Mon Sep 17 00:00:00 2001
From: jD91mZM2 <me@krake.one>
Date: Thu, 1 Nov 2018 10:57:39 +0100
Subject: [PATCH] Fix getdents on redox

---
 src/header/dirent/mod.rs  |  20 ++-----
 src/platform/redox/mod.rs | 111 ++++++++++++++++++++++++++------------
 2 files changed, 80 insertions(+), 51 deletions(-)

diff --git a/src/header/dirent/mod.rs b/src/header/dirent/mod.rs
index b2a133bd..fadb8576 100644
--- a/src/header/dirent/mod.rs
+++ b/src/header/dirent/mod.rs
@@ -21,9 +21,8 @@ pub struct DIR {
     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,
+    // The last value of d_off, used by telldir
+    offset: usize
 }
 
 #[repr(C)]
@@ -89,20 +88,7 @@ pub unsafe extern "C" fn readdir(dir: *mut DIR) -> *mut dirent {
 
     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 += mem::size_of::<dirent>();
-        }
-        (*ptr).d_off = (*dir).offset as off_t;
-    }
-    #[cfg(not(target_os = "redox"))]
-    {
-        (*dir).offset = (*ptr).d_off as usize;
-    }
-
+    (*dir).offset = (*ptr).d_off as usize;
     (*dir).index += (*ptr).d_reclen as usize;
     ptr
 }
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
index 92972a89..df4f67ad 100644
--- a/src/platform/redox/mod.rs
+++ b/src/platform/redox/mod.rs
@@ -418,50 +418,93 @@ impl Pal for Sys {
         }
     }
 
-    fn getdents(fd: c_int, mut dirents: *mut dirent, mut bytes: usize) -> c_int {
-        let mut amount = 0;
+    fn getdents(fd: c_int, mut dirents: *mut dirent, max_bytes: usize) -> c_int {
+        // Get initial reading position
+        let mut read = match syscall::lseek(fd as usize, 0, SEEK_CUR) {
+            Ok(pos) => pos as isize,
+            Err(err) => return -err.errno
+        };
 
+        let mut written = 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,
+        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
+            let size = mem::size_of::<dirent>() - name.len().saturating_sub(*i + 1);
+            if *written + size > max_bytes {
+                // Seek back to after last read entry and return
+                match syscall::lseek(fd as usize, read, SEEK_SET as usize) {
+                    Ok(_) => return Some(*written as c_int),
+                    Err(err) => return Some(-err.errno)
+                }
+            }
+            unsafe {
+                *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
+        };
 
-            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,
+        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)
                     };
-                    dirents = dirents.offset(1);
-                }
-                amount += 1;
-                if bytes <= mem::size_of::<dirent>() {
-                    return amount;
+                    name[..copy].copy_from_slice(buf);
                 }
-                bytes -= mem::size_of::<dirent>();
-            } else {
-                if nindex < name.len() {
-                    name[nindex] = buf[bindex] as c_char;
+
+                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;
+                    }
                 }
-                nindex += 1;
-                bindex += 1;
             }
         }
     }
-- 
GitLab