Fix getdents on redox

This commit is contained in:
jD91mZM2
2018-11-01 10:57:39 +01:00
parent 09bd4bc375
commit 571b4d4976
2 changed files with 80 additions and 51 deletions
+3 -17
View File
@@ -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
}
+77 -34
View File
@@ -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;
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
};
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,
};
}
// 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
};
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,
// 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);
name[..copy].copy_from_slice(buf);
}
amount += 1;
if bytes <= mem::size_of::<dirent>() {
return amount;
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;
}
}
bytes -= mem::size_of::<dirent>();
} else {
if nindex < name.len() {
name[nindex] = buf[bindex] as c_char;
}
nindex += 1;
bindex += 1;
}
}
}