use crate::backend::fs::syscalls::getdents_uninit;
use crate::fd::AsFd;
use crate::ffi::CStr;
use crate::fs::FileType;
use crate::io;
use core::fmt;
use core::mem::{align_of, MaybeUninit};
#[cfg(not(linux_raw_dep))]
use libc::dirent64 as linux_dirent64;
#[cfg(linux_raw_dep)]
use linux_raw_sys::general::linux_dirent64;
pub struct RawDir<'buf, Fd: AsFd> {
fd: Fd,
buf: &'buf mut [MaybeUninit<u8>],
initialized: usize,
offset: usize,
}
impl<'buf, Fd: AsFd> RawDir<'buf, Fd> {
pub fn new(fd: Fd, buf: &'buf mut [MaybeUninit<u8>]) -> Self {
Self {
fd,
buf: {
let offset = buf.as_ptr().align_offset(align_of::<linux_dirent64>());
if offset < buf.len() {
&mut buf[offset..]
} else {
&mut []
}
},
initialized: 0,
offset: 0,
}
}
}
pub struct RawDirEntry<'a> {
file_name: &'a CStr,
file_type: u8,
inode_number: u64,
next_entry_cookie: i64,
}
impl<'a> fmt::Debug for RawDirEntry<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("RawDirEntry");
f.field("file_name", &self.file_name());
f.field("file_type", &self.file_type());
f.field("ino", &self.ino());
f.field("next_entry_cookie", &self.next_entry_cookie());
f.finish()
}
}
impl<'a> RawDirEntry<'a> {
#[inline]
pub fn file_name(&self) -> &CStr {
self.file_name
}
#[inline]
pub fn file_type(&self) -> FileType {
FileType::from_dirent_d_type(self.file_type)
}
#[inline]
#[doc(alias = "inode_number")]
pub fn ino(&self) -> u64 {
self.inode_number
}
#[inline]
#[doc(alias = "off")]
pub fn next_entry_cookie(&self) -> u64 {
self.next_entry_cookie as u64
}
}
impl<'buf, Fd: AsFd> RawDir<'buf, Fd> {
#[allow(unsafe_code)]
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<io::Result<RawDirEntry<'_>>> {
if self.is_buffer_empty() {
match getdents_uninit(self.fd.as_fd(), self.buf) {
Ok(0) => return None,
Ok(bytes_read) => {
self.initialized = bytes_read;
self.offset = 0;
}
Err(e) => return Some(Err(e)),
}
}
let dirent_ptr = self.buf[self.offset..].as_ptr();
let dirent = unsafe { &*dirent_ptr.cast::<linux_dirent64>() };
self.offset += usize::from(dirent.d_reclen);
Some(Ok(RawDirEntry {
file_type: dirent.d_type,
inode_number: dirent.d_ino.into(),
next_entry_cookie: dirent.d_off.into(),
file_name: unsafe { CStr::from_ptr(dirent.d_name.as_ptr().cast()) },
}))
}
pub fn is_buffer_empty(&self) -> bool {
self.offset >= self.initialized
}
}
fn _doctest() {}