use core::num;
use crate::protocol::prelude::*;
use crate::protocol::read::fuse_read_in_v7p1;
#[cfg(test)]
mod readdir_test;
pub struct ReaddirRequest<'a> {
phantom: PhantomData<&'a ()>,
node_id: NodeId,
size: u32,
cursor: Option<num::NonZeroU64>,
handle: u64,
opendir_flags: u32,
}
impl ReaddirRequest<'_> {
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub fn size(&self) -> u32 {
self.size
}
pub fn cursor(&self) -> Option<num::NonZeroU64> {
self.cursor
}
pub fn handle(&self) -> u64 {
self.handle
}
pub fn opendir_flags(&self) -> u32 {
self.opendir_flags
}
}
impl fmt::Debug for ReaddirRequest<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("ReaddirRequest")
.field("node_id", &self.node_id)
.field("size", &self.size)
.field("cursor", &format_args!("{:?}", self.cursor))
.field("handle", &self.handle)
.field("opendir_flags", &DebugHexU32(self.opendir_flags))
.finish()
}
}
impl<'a> fuse_io::DecodeRequest<'a> for ReaddirRequest<'a> {
fn decode_request(
mut dec: fuse_io::RequestDecoder<'a>,
) -> Result<Self, Error> {
let header = dec.header();
debug_assert!(header.opcode == fuse_kernel::FUSE_READDIR);
let node_id = try_node_id(header.nodeid)?;
if dec.version().minor() < 9 {
let raw: &'a fuse_read_in_v7p1 = dec.next_sized()?;
return Ok(Self {
phantom: PhantomData,
node_id,
size: raw.size,
cursor: num::NonZeroU64::new(raw.offset),
handle: raw.fh,
opendir_flags: 0,
});
}
let raw: &'a fuse_kernel::fuse_read_in = dec.next_sized()?;
Ok(Self {
phantom: PhantomData,
node_id,
size: raw.size,
cursor: num::NonZeroU64::new(raw.offset),
handle: raw.fh,
opendir_flags: raw.flags,
})
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ReaddirError {
kind: ReaddirErrorKind,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum ReaddirErrorKind {
ExceedsCapacity(usize, usize),
OverflowsUsize,
}
impl ReaddirError {
fn exceeds_capacity(response_size: usize, capacity: usize) -> ReaddirError {
ReaddirError {
kind: ReaddirErrorKind::ExceedsCapacity(response_size, capacity),
}
}
fn overflows_usize() -> ReaddirError {
ReaddirError {
kind: ReaddirErrorKind::OverflowsUsize,
}
}
}
impl fmt::Display for ReaddirError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::Debug::fmt(self, fmt)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ReaddirError {}
pub struct ReaddirResponse<'a> {
buf: ReaddirBuf<'a, fuse_kernel::fuse_dirent>,
}
impl ReaddirResponse<'_> {
pub const EMPTY: &'static ReaddirResponse<'static> = &ReaddirResponse {
buf: ReaddirBuf::None,
};
}
impl<'a> ReaddirResponse<'a> {
#[cfg(feature = "std")]
#[cfg_attr(doc, doc(cfg(feature = "std")))]
pub fn with_max_size(max_size: u32) -> ReaddirResponse<'a> {
let max_size = max_size as usize;
Self {
buf: ReaddirBuf::Owned {
cap: Vec::new(),
max_size,
},
}
}
pub fn with_capacity(capacity: &'a mut [u8]) -> ReaddirResponse<'a> {
let offset = capacity.as_ptr().align_offset(mem::align_of::<u64>());
if offset != 0 {
panic!(
"ReaddirResponse::with_capacity() requires an 8-byte aligned buffer."
);
}
Self {
buf: ReaddirBuf::Borrowed {
cap: capacity,
size: 0,
phantom: PhantomData,
},
}
}
pub fn entries(&self) -> impl Iterator<Item = &ReaddirEntry> {
ReaddirEntriesIter::new(&self.buf)
}
pub fn add_entry(
&mut self,
node_id: NodeId,
name: &NodeName,
cursor: num::NonZeroU64,
) -> &mut ReaddirEntry {
self.try_add_entry(node_id, name, cursor).unwrap()
}
pub fn try_add_entry(
&mut self,
node_id: NodeId,
name: &NodeName,
cursor: num::NonZeroU64,
) -> Result<&mut ReaddirEntry, ReaddirError> {
let name = name.as_bytes();
let dirent_buf = self.buf.try_alloc_dirent(name)?;
unsafe {
let dirent_ptr = dirent_buf as *mut fuse_kernel::fuse_dirent;
let name_ptr =
dirent_buf.add(size_of::<fuse_kernel::fuse_dirent>());
let padding_ptr = name_ptr.add(name.len());
dirent_ptr.write(fuse_kernel::fuse_dirent {
ino: node_id.get(),
off: cursor.get(),
namelen: name.len() as u32,
r#type: FileType::Unknown.as_bits(),
name: [],
});
ptr::copy_nonoverlapping(name.as_ptr(), name_ptr, name.len());
let padding_len = (8 - (name.len() % 8)) % 8;
if padding_len > 0 {
ptr::write_bytes(padding_ptr, 0, padding_len);
}
Ok(ReaddirEntry::new_ref_mut(&mut *dirent_ptr))
}
}
}
enum ReaddirBuf<'a, Dirent> {
None,
#[cfg(feature = "std")]
Owned {
cap: Vec<u8>,
max_size: usize,
},
Borrowed {
cap: &'a mut [u8],
size: usize,
phantom: PhantomData<&'a Dirent>,
},
}
trait DirentT {
fn namelen(&self) -> u32;
}
impl DirentT for fuse_kernel::fuse_dirent {
fn namelen(&self) -> u32 {
self.namelen
}
}
impl<Dirent: DirentT> ReaddirBuf<'_, Dirent> {
fn try_alloc_dirent(
&mut self,
name: &[u8],
) -> Result<*mut u8, ReaddirError> {
debug_assert!(name.len() > 0);
let padding_len = (8 - (name.len() % 8)) % 8;
let overhead = padding_len + size_of::<Dirent>();
let entry_size = overhead + name.len();
let entry_buf = match self {
ReaddirBuf::None => {
return Err(ReaddirError::exceeds_capacity(entry_size, 0));
},
ReaddirBuf::Borrowed {
cap,
size: size_ref,
..
} => {
let current_size: usize = *size_ref;
let new_size = match current_size.checked_add(entry_size) {
Some(x) => x,
None => return Err(ReaddirError::overflows_usize()),
};
if new_size > cap.len() {
return Err(ReaddirError::exceeds_capacity(
new_size,
cap.len(),
));
}
let (_, remaining_cap) = cap.split_at_mut(current_size);
let (entry_buf, _) = remaining_cap.split_at_mut(entry_size);
*size_ref = new_size;
entry_buf
},
#[cfg(feature = "std")]
Self::Owned { cap, max_size } => {
let current_size = cap.len();
let new_size = match current_size.checked_add(entry_size) {
Some(x) => x,
None => return Err(ReaddirError::overflows_usize()),
};
if new_size > *max_size {
return Err(ReaddirError::exceeds_capacity(
new_size, *max_size,
));
}
cap.resize(new_size, 0u8);
let (_, entry_buf) = cap.split_at_mut(current_size);
entry_buf
},
};
debug_assert!(
entry_buf.len() == entry_size,
"{} == {}",
entry_buf.len(),
entry_size,
);
Ok(entry_buf.as_mut_ptr())
}
fn next_dirent(&self, offset: usize) -> Option<(&Dirent, usize)> {
let mut buf = match &self {
Self::None => &[],
#[cfg(feature = "std")]
Self::Owned { cap, .. } => cap.as_slice(),
Self::Borrowed { cap, size, .. } => {
let (used, _) = cap.split_at(*size);
used
},
};
if offset == buf.len() {
return None;
}
if offset > 0 {
let (_, buf_offset) = buf.split_at(offset);
buf = buf_offset;
}
let dirent_size = mem::size_of::<Dirent>();
debug_assert!(buf.len() >= dirent_size);
let dirent = unsafe { &*(buf.as_ptr() as *const Dirent) };
let name_len = dirent.namelen() as usize;
let padding = (8 - (name_len % 8)) % 8;
let entry_span = dirent_size + name_len + padding;
return Some((dirent, offset + entry_span));
}
}
#[repr(transparent)]
pub struct ReaddirEntry(fuse_kernel::fuse_dirent);
impl ReaddirEntry {
pub(crate) fn new_ref(raw: &fuse_kernel::fuse_dirent) -> &ReaddirEntry {
unsafe {
&*(raw as *const fuse_kernel::fuse_dirent as *const ReaddirEntry)
}
}
pub(crate) fn new_ref_mut(
raw: &mut fuse_kernel::fuse_dirent,
) -> &mut ReaddirEntry {
unsafe {
&mut *(raw as *mut fuse_kernel::fuse_dirent as *mut ReaddirEntry)
}
}
pub fn node_id(&self) -> NodeId {
unsafe { NodeId::new_unchecked(self.0.ino) }
}
pub fn name(&self) -> &[u8] {
dirent_name(&self.0)
}
pub fn cursor(&self) -> num::NonZeroU64 {
unsafe { num::NonZeroU64::new_unchecked(self.0.off) }
}
pub fn file_type(&self) -> FileType {
dirent_type(&self.0)
}
pub fn set_file_type(&mut self, file_type: FileType) {
self.0.r#type = file_type.as_bits();
}
}
fn dirent_type(dirent: &fuse_kernel::fuse_dirent) -> FileType {
match FileType::from_bits(dirent.r#type) {
Some(t) => t,
None => unsafe {
core::hint::unreachable_unchecked()
},
}
}
fn dirent_name(dirent: &fuse_kernel::fuse_dirent) -> &[u8] {
unsafe {
core::slice::from_raw_parts(
&dirent.name as *const u8,
dirent.namelen as usize,
)
}
}
fn dirent_fmt(
dirent: &fuse_kernel::fuse_dirent,
fmt: &mut fmt::Formatter,
) -> fmt::Result {
fmt.debug_struct("ReaddirEntry")
.field("node_id", &dirent.ino)
.field("cursor", &dirent.off)
.field("file_type", &dirent_type(dirent))
.field("name", &DebugBytesAsString(dirent_name(dirent)))
.finish()
}
impl fmt::Debug for ReaddirEntry {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
dirent_fmt(&self.0, fmt)
}
}
struct ReaddirEntriesIter<'a> {
buf: &'a ReaddirBuf<'a, fuse_kernel::fuse_dirent>,
offset: usize,
}
impl<'a> ReaddirEntriesIter<'a> {
fn new(buf: &'a ReaddirBuf<'a, fuse_kernel::fuse_dirent>) -> Self {
Self { buf, offset: 0 }
}
}
impl<'a> core::iter::Iterator for ReaddirEntriesIter<'a> {
type Item = &'a ReaddirEntry;
fn next(&mut self) -> Option<&'a ReaddirEntry> {
match self.buf.next_dirent(self.offset) {
None => None,
Some((dirent, new_offset)) => {
self.offset = new_offset;
Some(ReaddirEntry::new_ref(dirent))
},
}
}
}
impl fmt::Debug for ReaddirResponse<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let entries = DebugClosure(|fmt| {
fmt.debug_list()
.entries(ReaddirEntriesIter::new(&self.buf))
.finish()
});
fmt.debug_struct("ReaddirResponse")
.field("entries", &entries)
.finish()
}
}
impl fuse_io::EncodeResponse for ReaddirResponse<'_> {
fn encode_response<'a, Chan: fuse_io::Channel>(
&'a self,
enc: fuse_io::ResponseEncoder<Chan>,
) -> Result<(), Chan::Error> {
match &self.buf {
ReaddirBuf::None => enc.encode_header_only(),
#[cfg(feature = "std")]
ReaddirBuf::Owned { cap, .. } => enc.encode_bytes(&cap),
ReaddirBuf::Borrowed { cap, size, .. } => {
let (bytes, _) = cap.split_at(*size);
enc.encode_bytes(bytes)
},
}
}
}