use super::file_serial_no;
use super::oshandle::RawOsHandle;
use crate::handle::{Advice, Dircookie, Dirent, Fdflags, Filesize, Filestat};
use crate::path;
use crate::sys::osdir::OsDir;
use crate::sys::osfile::OsFile;
use crate::sys::AsFile;
use crate::Result;
use std::convert::TryInto;
use std::fs::{File, OpenOptions};
use std::os::windows::fs::OpenOptionsExt;
use std::os::windows::prelude::{AsRawHandle, FromRawHandle};
use std::path::Path;
use tracing::trace;
use winx::file::{AccessMode, FileModeInformation, Flags};
pub(crate) fn fdstat_get(file: &File) -> Result<Fdflags> {
let mut fdflags = Fdflags::empty();
let handle = file.as_raw_handle();
let access_mode = winx::file::query_access_information(handle)?;
let mode = winx::file::query_mode_information(handle)?;
if access_mode.contains(AccessMode::FILE_APPEND_DATA)
&& !access_mode.contains(AccessMode::FILE_WRITE_DATA)
{
fdflags |= Fdflags::APPEND;
}
if mode.contains(FileModeInformation::FILE_WRITE_THROUGH) {
fdflags |= Fdflags::SYNC;
}
Ok(fdflags)
}
pub(crate) fn fdstat_set_flags(file: &File, fdflags: Fdflags) -> Result<Option<RawOsHandle>> {
let handle = file.as_raw_handle();
let access_mode = winx::file::query_access_information(handle)?;
let new_access_mode = file_access_mode_from_fdflags(
fdflags,
access_mode.contains(AccessMode::FILE_READ_DATA),
access_mode.contains(AccessMode::FILE_WRITE_DATA)
| access_mode.contains(AccessMode::FILE_APPEND_DATA),
);
unsafe {
Ok(Some(RawOsHandle::from_raw_handle(winx::file::reopen_file(
handle,
new_access_mode,
fdflags.into(),
)?)))
}
}
pub(crate) fn advise(
_file: &OsFile,
_advice: Advice,
_offset: Filesize,
_len: Filesize,
) -> Result<()> {
Ok(())
}
fn file_access_mode_from_fdflags(fdflags: Fdflags, read: bool, write: bool) -> AccessMode {
let mut access_mode = AccessMode::READ_CONTROL;
if read {
access_mode.insert(AccessMode::FILE_GENERIC_READ);
}
if write {
access_mode.insert(AccessMode::FILE_GENERIC_WRITE);
}
if fdflags.contains(&Fdflags::APPEND) {
access_mode.insert(AccessMode::FILE_APPEND_DATA);
access_mode.remove(AccessMode::FILE_WRITE_DATA);
}
access_mode
}
pub(crate) fn readdir(
dirfd: &OsDir,
cookie: Dircookie,
) -> Result<Box<dyn Iterator<Item = Result<(Dirent, String)>>>> {
use winx::file::get_file_path;
let cookie = cookie.try_into()?;
let path = get_file_path(&*dirfd.as_file()?)?;
let path = Path::new(&path);
let parent = path.parent().unwrap_or(path);
let dot = dirent_from_path(path, ".", 1)?;
let dotdot = dirent_from_path(parent, "..", 2)?;
trace!(" | fd_readdir impl: executing std::fs::ReadDir");
let iter = path.read_dir()?.zip(3..).map(|(dir, no)| {
let dir: std::fs::DirEntry = dir?;
let ftype = dir.file_type()?;
let name = path::from_host(dir.file_name())?;
let d_ino = File::open(dir.path()).and_then(|f| file_serial_no(&f))?;
let dirent = Dirent {
d_namlen: name.len().try_into()?,
d_type: ftype.into(),
d_ino,
d_next: no,
};
Ok((dirent, name))
});
let iter = vec![dot, dotdot].into_iter().map(Ok).chain(iter);
Ok(Box::new(iter.skip(cookie)))
}
fn dirent_from_path<P: AsRef<Path>>(
path: P,
name: &str,
cookie: Dircookie,
) -> Result<(Dirent, String)> {
let path = path.as_ref();
trace!("dirent_from_path: opening {}", path.to_string_lossy());
let file = OpenOptions::new()
.custom_flags(Flags::FILE_FLAG_BACKUP_SEMANTICS.bits())
.read(true)
.open(path)?;
let ty = file.metadata()?.file_type();
let name = name.to_owned();
let dirent = Dirent {
d_namlen: name.len().try_into()?,
d_next: cookie,
d_type: ty.into(),
d_ino: file_serial_no(&file)?,
};
Ok((dirent, name))
}
pub(crate) fn filestat_get(file: &File) -> Result<Filestat> {
let filestat = file.try_into()?;
Ok(filestat)
}