c-scape 0.22.3

A libc bottom-half implementation in Rust
Documentation
use core::mem::{offset_of, transmute};
use core::ptr::null_mut;

use errno::{set_errno, Errno};
use libc::{c_int, c_void};

use super::CScapeDir;

#[no_mangle]
unsafe extern "C" fn readdir64_r(
    dir: *mut c_void,
    entry: *mut libc::dirent64,
    ptr: *mut *mut libc::dirent64,
) -> c_int {
    libc!(libc::readdir64_r(dir.cast(), entry, ptr));

    let c_scape_dir = dir.cast::<CScapeDir>();
    let dir = &mut (*c_scape_dir).dir;
    match dir.read() {
        None => {
            *ptr = null_mut();
            0
        }
        Some(Ok(e)) => {
            let file_type = match e.file_type() {
                rustix::fs::FileType::RegularFile => libc::DT_REG,
                rustix::fs::FileType::Directory => libc::DT_DIR,
                rustix::fs::FileType::Symlink => libc::DT_LNK,
                rustix::fs::FileType::Fifo => libc::DT_FIFO,
                rustix::fs::FileType::Socket => libc::DT_SOCK,
                rustix::fs::FileType::CharacterDevice => libc::DT_CHR,
                rustix::fs::FileType::BlockDevice => libc::DT_BLK,
                rustix::fs::FileType::Unknown => libc::DT_UNKNOWN,
            };
            *entry = libc::dirent64 {
                d_ino: e.ino(),
                d_off: 0, // We don't implement `seekdir` yet anyway.
                d_reclen: (offset_of!(libc::dirent64, d_name) + e.file_name().to_bytes().len() + 1)
                    .try_into()
                    .unwrap(),
                d_type: file_type,
                d_name: [0; 256],
            };
            let len = core::cmp::min(256, e.file_name().to_bytes().len());
            (&mut *entry).d_name[..len].copy_from_slice(transmute(e.file_name().to_bytes()));
            *ptr = entry;
            0
        }
        Some(Err(err)) => err.raw_os_error(),
    }
}

#[no_mangle]
unsafe extern "C" fn readdir64(dir: *mut libc::DIR) -> *mut libc::dirent64 {
    libc!(libc::readdir64(dir.cast()));

    let c_scape_dir = dir.cast::<CScapeDir>();
    let dir = &mut (*c_scape_dir).dir;
    match dir.read() {
        None => null_mut(),
        Some(Ok(e)) => {
            let file_type = match e.file_type() {
                rustix::fs::FileType::RegularFile => libc::DT_REG,
                rustix::fs::FileType::Directory => libc::DT_DIR,
                rustix::fs::FileType::Symlink => libc::DT_LNK,
                rustix::fs::FileType::Fifo => libc::DT_FIFO,
                rustix::fs::FileType::Socket => libc::DT_SOCK,
                rustix::fs::FileType::CharacterDevice => libc::DT_CHR,
                rustix::fs::FileType::BlockDevice => libc::DT_BLK,
                rustix::fs::FileType::Unknown => libc::DT_UNKNOWN,
            };
            (*c_scape_dir).storage.dirent64 = libc::dirent64 {
                d_ino: e.ino(),
                d_off: 0, // We don't implement `seekdir` yet anyway.
                d_reclen: (offset_of!(libc::dirent64, d_name) + e.file_name().to_bytes().len() + 1)
                    .try_into()
                    .unwrap(),
                d_type: file_type,
                d_name: [0; 256],
            };
            let len = core::cmp::min(256, e.file_name().to_bytes().len());
            (&mut *c_scape_dir).storage.dirent64.d_name[..len]
                .copy_from_slice(transmute(e.file_name().to_bytes()));
            &mut (*c_scape_dir).storage.dirent64
        }
        Some(Err(err)) => {
            set_errno(Errno(err.raw_os_error()));
            null_mut()
        }
    }
}

#[no_mangle]
unsafe extern "C" fn readdir(dir: *mut libc::DIR) -> *mut libc::dirent {
    libc!(libc::readdir(dir.cast()));

    let c_scape_dir = dir.cast::<CScapeDir>();
    let dir = &mut (*c_scape_dir).dir;
    match dir.read() {
        None => null_mut(),
        Some(Ok(e)) => {
            let file_type = match e.file_type() {
                rustix::fs::FileType::RegularFile => libc::DT_REG,
                rustix::fs::FileType::Directory => libc::DT_DIR,
                rustix::fs::FileType::Symlink => libc::DT_LNK,
                rustix::fs::FileType::Fifo => libc::DT_FIFO,
                rustix::fs::FileType::Socket => libc::DT_SOCK,
                rustix::fs::FileType::CharacterDevice => libc::DT_CHR,
                rustix::fs::FileType::BlockDevice => libc::DT_BLK,
                rustix::fs::FileType::Unknown => libc::DT_UNKNOWN,
            };

            (*c_scape_dir).storage.dirent = libc::dirent {
                d_ino: match e.ino().try_into() {
                    Ok(ino) => ino,
                    Err(_) => {
                        set_errno(Errno(libc::EOVERFLOW));
                        return null_mut();
                    }
                },
                d_off: 0, // We don't implement `seekdir` yet anyway.
                d_reclen: (offset_of!(libc::dirent64, d_name) + e.file_name().to_bytes().len() + 1)
                    .try_into()
                    .unwrap(),
                d_type: file_type,
                d_name: [0; 256],
            };

            let len = core::cmp::min(256, e.file_name().to_bytes().len());
            (&mut *c_scape_dir).storage.dirent.d_name[..len]
                .copy_from_slice(transmute(e.file_name().to_bytes()));
            &mut (*c_scape_dir).storage.dirent
        }
        Some(Err(err)) => {
            set_errno(Errno(err.raw_os_error()));
            null_mut()
        }
    }
}

#[cfg(feature = "todo")]
#[no_mangle]
unsafe extern "C" fn rewinddir(dir: *mut libc::DIR) {
    libc!(libc::rewinddir(dir));

    let c_scape_dir = dir.cast::<CScapeDir>();
    (*c_scape_dir).dir.rewind();
}

#[cfg(feature = "todo")]
#[no_mangle]
unsafe extern "C" fn scandir() {
    //libc!(libc::scandir());
    todo!("scandir")
}

#[cfg(feature = "todo")]
#[no_mangle]
unsafe extern "C" fn seekdir() {
    //libc!(libc::seekdir());
    todo!("seekdir")
}

#[cfg(feature = "todo")]
#[no_mangle]
unsafe extern "C" fn telldir() {
    //libc!(libc::telldir());
    todo!("telldir")
}