use std::{io, os::windows::fs::MetadataExt, path::Path, time::SystemTime};
#[cfg(dirfd)]
use compio_driver::ToSharedFd;
use compio_runtime::ResumeUnwind;
#[cfg(dirfd)]
use crate::File;
pub async fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
let path = path.as_ref().to_path_buf();
compio_runtime::spawn_blocking(move || std::fs::metadata(path))
.await
.resume_unwind()
.expect("shouldn't be cancelled")
.map(Metadata::from)
}
pub async fn symlink_metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
let path = path.as_ref().to_path_buf();
compio_runtime::spawn_blocking(move || std::fs::symlink_metadata(path))
.await
.resume_unwind()
.expect("shouldn't be cancelled")
.map(Metadata::from)
}
#[cfg(dirfd)]
async fn metadata_at_impl(
dir: &File,
path: impl AsRef<Path>,
follow_symlinks: bool,
) -> io::Result<Metadata> {
let path = path.as_ref().to_path_buf();
crate::spawn_blocking_with(dir.to_shared_fd(), move |dir| {
cap_primitives::fs::stat(
dir,
&path,
if follow_symlinks {
cap_primitives::fs::FollowSymlinks::Yes
} else {
cap_primitives::fs::FollowSymlinks::No
},
)
})
.await
.map(Metadata::from)
}
#[cfg(dirfd)]
pub async fn metadata_at(dir: &File, path: impl AsRef<Path>) -> io::Result<Metadata> {
metadata_at_impl(dir, path, true).await
}
#[cfg(dirfd)]
pub async fn symlink_metadata_at(dir: &File, path: impl AsRef<Path>) -> io::Result<Metadata> {
metadata_at_impl(dir, path, false).await
}
pub async fn set_permissions(path: impl AsRef<Path>, perm: Permissions) -> io::Result<()> {
let path = path.as_ref().to_path_buf();
compio_runtime::spawn_blocking(move || {
if let Some(p) = perm.original {
std::fs::set_permissions(&path, p)
} else {
let mut p = std::fs::metadata(&path)?.permissions();
p.set_readonly(perm.readonly());
std::fs::set_permissions(&path, p)
}
})
.await
.resume_unwind()
.expect("shouldn't be cancelled")
}
#[derive(Clone)]
pub struct Metadata {
attributes: u32,
creation_time: u64,
last_access_time: u64,
last_write_time: u64,
file_size: u64,
#[cfg(feature = "windows_by_handle")]
volume_serial_number: Option<u32>,
#[cfg(feature = "windows_by_handle")]
number_of_links: Option<u32>,
#[cfg(feature = "windows_by_handle")]
file_index: Option<u64>,
file_type: FileType,
permissions: Permissions,
modified: SystemTime,
accessed: SystemTime,
created: SystemTime,
}
impl Metadata {
pub fn file_type(&self) -> FileType {
self.file_type
}
pub fn is_dir(&self) -> bool {
self.file_type.is_dir()
}
pub fn is_file(&self) -> bool {
self.file_type.is_file()
}
pub fn is_symlink(&self) -> bool {
self.file_type.is_symlink()
}
pub fn len(&self) -> u64 {
self.file_size
}
pub fn permissions(&self) -> Permissions {
self.permissions.clone()
}
pub fn modified(&self) -> io::Result<SystemTime> {
Ok(self.modified)
}
pub fn accessed(&self) -> io::Result<SystemTime> {
Ok(self.accessed)
}
pub fn created(&self) -> io::Result<SystemTime> {
Ok(self.created)
}
pub fn file_attributes(&self) -> u32 {
self.attributes
}
pub fn creation_time(&self) -> u64 {
self.creation_time
}
pub fn last_access_time(&self) -> u64 {
self.last_access_time
}
pub fn last_write_time(&self) -> u64 {
self.last_write_time
}
#[cfg(feature = "windows_by_handle")]
pub fn volume_serial_number(&self) -> Option<u32> {
self.volume_serial_number
}
#[cfg(feature = "windows_by_handle")]
pub fn number_of_links(&self) -> Option<u32> {
self.number_of_links
}
#[cfg(feature = "windows_by_handle")]
pub fn file_index(&self) -> Option<u64> {
self.file_index
}
}
impl From<std::fs::Metadata> for Metadata {
fn from(value: std::fs::Metadata) -> Self {
Self {
attributes: value.file_attributes(),
creation_time: value.creation_time(),
last_access_time: value.last_access_time(),
last_write_time: value.last_write_time(),
file_size: value.file_size(),
#[cfg(feature = "windows_by_handle")]
volume_serial_number: value.volume_serial_number(),
#[cfg(feature = "windows_by_handle")]
number_of_links: value.number_of_links(),
#[cfg(feature = "windows_by_handle")]
file_index: value.file_index(),
file_type: value.file_type().into(),
permissions: value.permissions().into(),
modified: value
.modified()
.expect("std::fs::Metadata::modified() should never fail on Windows"),
accessed: value
.accessed()
.expect("std::fs::Metadata::accessed() should never fail on Windows"),
created: value
.created()
.expect("std::fs::Metadata::created() should never fail on Windows"),
}
}
}
#[cfg(dirfd)]
impl From<cap_primitives::fs::Metadata> for Metadata {
fn from(value: cap_primitives::fs::Metadata) -> Self {
use cap_primitives::fs::MetadataExt;
Self {
attributes: value.file_attributes(),
creation_time: value.creation_time(),
last_access_time: value.last_access_time(),
last_write_time: value.last_write_time(),
file_size: value.file_size(),
#[cfg(feature = "windows_by_handle")]
volume_serial_number: value.volume_serial_number(),
#[cfg(feature = "windows_by_handle")]
number_of_links: value.number_of_links(),
#[cfg(feature = "windows_by_handle")]
file_index: value.file_index(),
file_type: value.file_type().into(),
permissions: value.permissions().into(),
modified: value
.modified()
.expect("cap_primitives::fs::Metadata::modified() should never fail on Windows")
.into_std(),
accessed: value
.accessed()
.expect("cap_primitives::fs::Metadata::accessed() should never fail on Windows")
.into_std(),
created: value
.created()
.expect("cap_primitives::fs::Metadata::created() should never fail on Windows")
.into_std(),
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Permissions {
readonly: bool,
pub(crate) original: Option<std::fs::Permissions>,
}
impl Permissions {
pub fn readonly(&self) -> bool {
self.readonly
}
pub fn set_readonly(&mut self, readonly: bool) {
self.readonly = readonly;
if let Some(p) = &mut self.original {
p.set_readonly(readonly);
}
}
}
impl From<std::fs::Permissions> for Permissions {
fn from(value: std::fs::Permissions) -> Self {
Self {
readonly: value.readonly(),
original: Some(value),
}
}
}
#[cfg(dirfd)]
impl From<cap_primitives::fs::Permissions> for Permissions {
fn from(value: cap_primitives::fs::Permissions) -> Self {
Self {
readonly: value.readonly(),
original: None,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct FileType {
is_dir: bool,
is_symlink: bool,
}
impl FileType {
pub fn is_dir(&self) -> bool {
self.is_dir && !self.is_symlink
}
pub fn is_file(&self) -> bool {
!self.is_dir && !self.is_symlink
}
pub fn is_symlink(&self) -> bool {
self.is_symlink
}
pub fn is_symlink_dir(&self) -> bool {
self.is_symlink && self.is_dir
}
pub fn is_symlink_file(&self) -> bool {
self.is_symlink && !self.is_dir
}
}
impl From<std::fs::FileType> for FileType {
fn from(value: std::fs::FileType) -> Self {
Self {
is_dir: value.is_dir(),
is_symlink: value.is_symlink(),
}
}
}
#[cfg(dirfd)]
impl From<cap_primitives::fs::FileType> for FileType {
fn from(value: cap_primitives::fs::FileType) -> Self {
Self {
is_dir: value.is_dir(),
is_symlink: value.is_symlink(),
}
}
}