use std::cell::UnsafeCell;
use widestring::{U16CStr, u16cstr};
use windows::Win32::Foundation::STATUS_SUCCESS;
use winfsp_sys::{
FSP_FSCTL_DIR_INFO, FspFileSystemAcquireDirectoryBufferEx, FspFileSystemAddDirInfo,
FspFileSystemDeleteDirectoryBuffer, FspFileSystemFillDirectoryBuffer,
FspFileSystemReadDirectoryBuffer, FspFileSystemReleaseDirectoryBuffer, PVOID,
};
use crate::error::Result;
use crate::filesystem::sealed::WideNameInfoInternal;
use crate::filesystem::{FileInfo, WideNameInfo, ensure_layout};
use crate::util::AssertThreadSafe;
#[derive(Debug)]
pub struct DirBuffer(AssertThreadSafe<UnsafeCell<PVOID>>);
#[derive(Debug)]
pub struct DirBufferLock<'a>(&'a DirBuffer);
#[derive(Debug)]
pub struct DirMarker<'a>(pub(crate) Option<&'a U16CStr>);
impl DirMarker<'_> {
pub fn reset(&mut self) {
self.0.take();
}
pub fn is_none(&self) -> bool {
self.0.is_none()
}
pub fn is_parent(&self) -> bool {
if let Some(marker) = self.0 {
return marker == u16cstr!("..");
}
false
}
pub fn is_current(&self) -> bool {
if let Some(marker) = self.0 {
return marker == u16cstr!(".");
}
false
}
pub fn inner(&self) -> Option<&[u16]> {
self.0.map(U16CStr::as_slice)
}
pub fn inner_as_cstr(&self) -> Option<&U16CStr> {
self.0
}
}
impl Default for DirBuffer {
fn default() -> Self {
Self::new()
}
}
impl DirBuffer {
pub fn new() -> Self {
Self(AssertThreadSafe(UnsafeCell::new(std::ptr::null_mut())))
}
pub fn acquire(&self, reset: bool, capacity_hint: Option<u32>) -> Result<DirBufferLock<'_>> {
let mut result = STATUS_SUCCESS;
unsafe {
if FspFileSystemAcquireDirectoryBufferEx(
self.0.0.get(),
reset.into(),
capacity_hint.unwrap_or(0),
&mut result.0,
) != 0
{
Ok(DirBufferLock(self))
} else {
Err(result.into())
}
}
}
pub fn read(&self, marker: DirMarker, buffer: &mut [u8]) -> u32 {
let mut out = 0u32;
unsafe {
FspFileSystemReadDirectoryBuffer(
self.0.0.get(),
marker
.0
.map_or(std::ptr::null_mut(), |v| v.as_ptr().cast_mut()),
buffer.as_mut_ptr() as *mut _,
buffer.len() as u32,
&mut out,
);
}
out
}
}
impl DirBufferLock<'_> {
pub fn write<const D: usize>(&self, dir_info: &mut DirInfo<D>) -> Result<()> {
let mut status = STATUS_SUCCESS;
unsafe {
let buffer = self.0;
if FspFileSystemFillDirectoryBuffer(
buffer.0.0.get(),
(dir_info as *mut DirInfo<D>).cast(),
&mut status.0,
) == 0
{
return Err(status.into());
}
}
Ok(())
}
}
impl Drop for DirBuffer {
fn drop(&mut self) {
unsafe {
FspFileSystemDeleteDirectoryBuffer(self.0.0.get());
}
}
}
impl Drop for DirBufferLock<'_> {
fn drop(&mut self) {
let buffer = self.0;
unsafe { FspFileSystemReleaseDirectoryBuffer(buffer.0.0.get()) }
}
}
#[repr(C)]
union DirInfoPadding {
next_offset: u64,
padding: [u8; 24],
}
#[repr(C)]
pub struct DirInfo<const BUFFER_SIZE: usize = 255> {
size: u16,
file_info: FileInfo,
padding: DirInfoPadding,
file_name: [u16; BUFFER_SIZE],
}
ensure_layout!(FSP_FSCTL_DIR_INFO, DirInfo<0>);
impl<const BUFFER_SIZE: usize> DirInfo<BUFFER_SIZE> {
pub fn new() -> Self {
Self {
size: std::mem::size_of::<DirInfo<0>>() as u16,
file_info: FileInfo::default(),
padding: DirInfoPadding { padding: [0; 24] },
file_name: [0; BUFFER_SIZE],
}
}
pub fn file_info_mut(&mut self) -> &mut FileInfo {
&mut self.file_info
}
}
impl<const BUFFER_SIZE: usize> Default for DirInfo<BUFFER_SIZE> {
fn default() -> Self {
Self::new()
}
}
impl<const BUFFER_SIZE: usize> WideNameInfoInternal<BUFFER_SIZE> for DirInfo<BUFFER_SIZE> {
fn name_buffer(&mut self) -> &mut [u16; BUFFER_SIZE] {
&mut self.file_name
}
fn set_size(&mut self, buffer_size: u16) {
self.size = std::mem::size_of::<DirInfo<0>>() as u16 + buffer_size
}
fn add_to_buffer_internal(entry: Option<&Self>, buffer: &mut [u8], cursor: &mut u32) -> bool {
unsafe {
if let Some(entry) = entry {
FspFileSystemAddDirInfo(
(entry as *const Self).cast_mut().cast(),
buffer.as_mut_ptr().cast(),
buffer.len() as u32,
cursor,
) != 0
} else {
FspFileSystemAddDirInfo(
std::ptr::null_mut(),
buffer.as_mut_ptr().cast(),
buffer.len() as u32,
cursor,
) != 0
}
}
}
}
impl<const BUFFER_SIZE: usize> WideNameInfo<BUFFER_SIZE> for DirInfo<BUFFER_SIZE> {
fn reset(&mut self) {
self.size = std::mem::size_of::<DirInfo<0>>() as u16;
self.file_info = FileInfo::default();
self.padding.next_offset = 0;
self.padding.padding = [0; 24];
self.file_name = [0; BUFFER_SIZE]
}
}