Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sys_getdents64 use maxlen(255), which should change to variable length #110

Open
MrRobertYuan opened this issue May 27, 2024 · 2 comments
Open
Assignees
Labels
bug Something isn't working

Comments

@MrRobertYuan
Copy link
Contributor

When running Python.
Tests use getdents64 to read a directory which has so many directory entries. But now the implements in RuxOS, can only read 7 entries at a time, so it has to invoke it a lot of times, which makes it slow.
So we have to change the implement of getdents64 in RuxOS.

@MrRobertYuan MrRobertYuan self-assigned this May 27, 2024
@coolyjg coolyjg added the bug Something isn't working label May 27, 2024
@minminm
Copy link
Contributor

minminm commented May 28, 2024

  1. add new_with_directory in ReadDir (modules/ruxfs/src/api/dir.rs)
    pub(super) fn new_with_directory(path: &'a str, dir: fops::Directory) -> Result<Self> {
        let mut opts = fops::OpenOptions::new();
        opts.read(true);
        const EMPTY: fops::DirEntry = fops::DirEntry::default();
        let dirent_buf = [EMPTY; 31];
        Ok(ReadDir {
            path,
            inner: dir,
            end_of_stream: false,
            buf_pos: 0,
            buf_end: 0,
            dirent_buf,
        })
    }
  1. add read_dir_by_directory (modules/ruxfs/src/api/mod.rs)
use crate::fops::Directory;
pub fn read_dir_by_directory(path: &str, dir: Directory) -> io::Result<ReadDir> {
    ReadDir::new_with_directory(path, dir)
}

  1. add LinuxDirent64 struct and reimplement sys_getdents64 (api/ruxos_posix_api/src/imp/fs.rs),
use ruxfs::{
    api::{set_current_dir, read_dir_by_directory},
    fops::{DirEntry, OpenOptions},
};
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct LinuxDirent64 {
    pub d_ino: u64,
    pub d_off: u64,
    pub d_reclen: u16,
    pub d_type: u8,
    pub d_name: [u8; 0],
}

impl LinuxDirent64 {
    pub fn fixed_size() -> usize {
        8 + 8 + 2 + 1
    }
    pub fn set_fixed_part(&mut self, ino: u64, off: u64, reclen: usize, type_: u8) {
        self.d_ino = ino;
        self.d_off = off;
        self.d_reclen = reclen as u16;
        self.d_type = type_;
    }
}


impl LinuxDirent64 {
    pub fn fixed_size() -> usize {
        8 + 8 + 2 + 1
    }
    pub fn set_fixed_part(&mut self, ino: u64, off: u64, reclen: usize, type_: u8) {
        self.d_ino = ino;
        self.d_off = off;
        self.d_reclen = reclen as u16;
        self.d_type = type_;
    }
}
pub unsafe fn sys_getdents64(
    fd: c_int,
    buf: *mut u8,
    len: ctypes::size_t,
) -> c_long {
    debug!(
        "sys_getdents64 <= fd: {}, buf: {:p}, len: {}",
        fd, buf, len
    );

    syscall_body!(sys_getdents64, {
        if len < LinuxDirent64::fixed_size() {
            // debug!("xsm, step1");
            return Err(LinuxError::EINVAL);
        }

        let mut all_offset = 0;
        let mut buf_offset = 0;
        loop {
            if buf_offset + LinuxDirent64::fixed_size() >= len {
                break;
            }
            let dir_ent = unsafe { *(buf.add(buf_offset) as *const LinuxDirent64) };
            if dir_ent.d_reclen == 0 {
                break;
            }
            buf_offset += dir_ent.d_reclen as usize;
            if all_offset < dir_ent.d_off {
                all_offset = dir_ent.d_off;
            } else {
                break;
            }
        }
        // debug!("xsm, step2");

        let buf = unsafe { core::slice::from_raw_parts_mut(buf, len) };

        // debug!("xsm, step2.5");

        let tmp = Directory::from_fd(fd)?;
        // debug!("xsm, step2.6");

        let dir = match Arc::try_unwrap(tmp) {
            Ok(inner) => inner,
            Err(_) => {
                // debug!("xsm, step2.7");
                debug!("Failed to get ownership of Directory because there are other references");
                return Err(LinuxError::EINVAL)
            }
        };

        // debug!("xsm, step3");

        let dir_iter = read_dir_by_directory("", dir.inner.into_inner()).unwrap();
        let mut count = 0;
        let mut offset: u64 = 0;

        // debug!("xsm, step4");

        for entry in dir_iter {
            let entry = entry.unwrap();
            let mut name = entry.file_name();
            name.push('\0');
            let name = name.as_bytes();
            let name_len = name.len();
            let file_type = entry.file_type();
            let entry_size = LinuxDirent64::fixed_size() + name_len + 1;
    
            if count + entry_size + LinuxDirent64::fixed_size() + 1 > len {
                debug!("buf not big enough");
                break;
            }
            offset += entry_size as u64;
            if offset <= all_offset {
                continue;
            }

            let dirent: &mut LinuxDirent64 = unsafe { &mut *(buf.as_mut_ptr().add(count) as *mut LinuxDirent64) };
            if file_type.is_dir() {
                dirent.set_fixed_part(1, offset, entry_size, 4u8);
            } else if file_type.is_file() {
                dirent.set_fixed_part(1, offset, entry_size, 8u8);
            } else {
                dirent.set_fixed_part(1, offset, entry_size, 0u8);
            }
    
            unsafe { core::ptr::copy_nonoverlapping(name.as_ptr(), dirent.d_name.as_mut_ptr(), name_len) };
    
            count += entry_size;
        }

        // debug!("xsm, step5");

    
        if count != 0 && count + LinuxDirent64::fixed_size() <= len {
            let dirent: &mut LinuxDirent64 = unsafe { &mut *(buf.as_mut_ptr().add(count) as *mut LinuxDirent64) };
            dirent.set_fixed_part(1, offset, LinuxDirent64::fixed_size(), 8u8);
            count += LinuxDirent64::fixed_size();
            return Ok(count - LinuxDirent64::fixed_size());
        }

        // debug!("xsm, step6");


        Ok(count)
    })
}

@coolyjg
Copy link
Contributor

coolyjg commented May 29, 2024

For alignment requirement, the size of LinuxDirent64 is actually 24 for C code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants