Skip to content

Commit

Permalink
feat(op, fs): ✨ Intergated statx into monoio (#268)
Browse files Browse the repository at this point in the history
* feat(op, fs): ✨ Intergated `statx` into monoio

* feat(op, fs): ✨ Intergated `statx` into monoio

* fix(op, fs): 🐛 fix some platform-specific bug

* fix(op, fs): 🐛 fix some platform-specific bug

* docs(fs): update permissions doc

* fix(op, fs): 🐛 fix some platform-specific bug

* fix(fs): 🐛 remove `unwrap` call that will cause panic

* feat: ✨ make `Statx`(actually is stat) support macos

* feat: ✨ make `Statx`(actually is stat) support macos

* fix(tests): 🐛 disable tests on windows

* ci: 📌 upgrade the cross version with latest git branch

upgrade the cross version with latest git branch, to fix some linker error when CI

* refactor: refact the impl structure for different os

* cross

* refactor: refact the impl structure for different os
  • Loading branch information
Lzzzzzt authored Jul 23, 2024
1 parent f3b27b1 commit dbdb3d8
Show file tree
Hide file tree
Showing 10 changed files with 1,106 additions and 0 deletions.
3 changes: 3 additions & 0 deletions monoio/src/driver/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ mod recv;
mod send;
mod write;

#[cfg(unix)]
mod statx;

#[cfg(all(target_os = "linux", feature = "splice"))]
mod splice;

Expand Down
212 changes: 212 additions & 0 deletions monoio/src/driver/op/statx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
use std::{ffi::CString, mem::MaybeUninit, path::Path};

#[cfg(all(target_os = "linux", feature = "iouring"))]
use io_uring::{opcode, types};
#[cfg(target_os = "linux")]
use libc::statx;

use super::{Op, OpAble};
#[cfg(any(feature = "legacy", feature = "poll-io"))]
use crate::driver::ready::Direction;
use crate::driver::{shared_fd::SharedFd, util::cstr};

#[derive(Debug)]
pub(crate) struct Statx<T> {
inner: T,
#[cfg(target_os = "linux")]
flags: i32,
#[cfg(target_os = "linux")]
statx_buf: Box<MaybeUninit<statx>>,
#[cfg(target_os = "macos")]
stat_buf: Box<MaybeUninit<libc::stat>>,
#[cfg(target_os = "macos")]
follow_symlinks: bool,
}

type FdStatx = Statx<SharedFd>;

impl Op<FdStatx> {
/// submit a statx operation
#[cfg(target_os = "linux")]
pub(crate) fn statx_using_fd(fd: &SharedFd, flags: i32) -> std::io::Result<Self> {
Op::submit_with(Statx {
inner: fd.clone(),
flags,
statx_buf: Box::new(MaybeUninit::uninit()),
})
}

#[cfg(target_os = "linux")]
pub(crate) async fn statx_result(self) -> std::io::Result<statx> {
let complete = self.await;
complete.meta.result?;

Ok(unsafe { MaybeUninit::assume_init(*complete.data.statx_buf) })
}

#[cfg(target_os = "macos")]
pub(crate) fn statx_using_fd(fd: &SharedFd, follow_symlinks: bool) -> std::io::Result<Self> {
Op::submit_with(Statx {
inner: fd.clone(),
follow_symlinks,
stat_buf: Box::new(MaybeUninit::uninit()),
})
}

#[cfg(target_os = "macos")]
pub(crate) async fn statx_result(self) -> std::io::Result<libc::stat> {
let complete = self.await;
complete.meta.result?;

Ok(unsafe { MaybeUninit::assume_init(*complete.data.stat_buf) })
}
}

impl OpAble for FdStatx {
#[cfg(all(target_os = "linux", feature = "iouring"))]
fn uring_op(&mut self) -> io_uring::squeue::Entry {
use std::os::fd::AsRawFd;

let statxbuf = self.statx_buf.as_mut_ptr() as *mut _;

opcode::Statx::new(types::Fd(self.inner.as_raw_fd()), c"".as_ptr(), statxbuf)
.flags(libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT)
.mask(libc::STATX_ALL)
.build()
}

#[cfg(any(feature = "legacy", feature = "poll-io"))]
fn legacy_interest(&self) -> Option<(crate::driver::ready::Direction, usize)> {
self.inner
.registered_index()
.map(|idx| (Direction::Read, idx))
}

#[cfg(all(any(feature = "legacy", feature = "poll-io"), target_os = "linux"))]
fn legacy_call(&mut self) -> std::io::Result<u32> {
use std::os::fd::AsRawFd;

use crate::syscall_u32;

syscall_u32!(statx(
self.inner.as_raw_fd(),
c"".as_ptr(),
libc::AT_EMPTY_PATH,
libc::STATX_ALL,
self.statx_buf.as_mut_ptr() as *mut _
))
}

#[cfg(all(any(feature = "legacy", feature = "poll-io"), windows))]
fn legacy_call(&mut self) -> std::io::Result<u32> {
unimplemented!()
}

#[cfg(all(any(feature = "legacy", feature = "poll-io"), target_os = "macos"))]
fn legacy_call(&mut self) -> std::io::Result<u32> {
use std::os::fd::AsRawFd;

use crate::syscall_u32;

syscall_u32!(fstat(
self.inner.as_raw_fd(),
self.stat_buf.as_mut_ptr() as *mut _
))
}
}

type PathStatx = Statx<CString>;

impl Op<PathStatx> {
/// submit a statx operation
#[cfg(target_os = "linux")]
pub(crate) fn statx_using_path<P: AsRef<Path>>(path: P, flags: i32) -> std::io::Result<Self> {
let path = cstr(path.as_ref())?;
Op::submit_with(Statx {
inner: path,
flags,
statx_buf: Box::new(MaybeUninit::uninit()),
})
}

#[cfg(target_os = "linux")]
pub(crate) async fn statx_result(self) -> std::io::Result<statx> {
let complete = self.await;
complete.meta.result?;

Ok(unsafe { MaybeUninit::assume_init(*complete.data.statx_buf) })
}

#[cfg(target_os = "macos")]
pub(crate) fn statx_using_path<P: AsRef<Path>>(
path: P,
follow_symlinks: bool,
) -> std::io::Result<Self> {
let path = cstr(path.as_ref())?;
Op::submit_with(Statx {
inner: path,
follow_symlinks,
stat_buf: Box::new(MaybeUninit::uninit()),
})
}

#[cfg(target_os = "macos")]
pub(crate) async fn statx_result(self) -> std::io::Result<libc::stat> {
let complete = self.await;
complete.meta.result?;

Ok(unsafe { MaybeUninit::assume_init(*complete.data.stat_buf) })
}
}

impl OpAble for PathStatx {
#[cfg(all(target_os = "linux", feature = "iouring"))]
fn uring_op(&mut self) -> io_uring::squeue::Entry {
let statxbuf = self.statx_buf.as_mut_ptr() as *mut _;

opcode::Statx::new(types::Fd(libc::AT_FDCWD), self.inner.as_ptr(), statxbuf)
.flags(self.flags)
.mask(libc::STATX_ALL)
.build()
}

#[cfg(any(feature = "legacy", feature = "poll-io"))]
fn legacy_interest(&self) -> Option<(crate::driver::ready::Direction, usize)> {
None
}

#[cfg(all(any(feature = "legacy", feature = "poll-io"), target_os = "linux"))]
fn legacy_call(&mut self) -> std::io::Result<u32> {
use crate::syscall_u32;

syscall_u32!(statx(
libc::AT_FDCWD,
self.inner.as_ptr(),
self.flags,
libc::STATX_ALL,
self.statx_buf.as_mut_ptr() as *mut _
))
}

#[cfg(all(any(feature = "legacy", feature = "poll-io"), windows))]
fn legacy_call(&mut self) -> std::io::Result<u32> {
unimplemented!()
}

#[cfg(all(any(feature = "legacy", feature = "poll-io"), target_os = "macos"))]
fn legacy_call(&mut self) -> std::io::Result<u32> {
use crate::syscall_u32;

if self.follow_symlinks {
syscall_u32!(stat(
self.inner.as_ptr(),
self.stat_buf.as_mut_ptr() as *mut _
))
} else {
syscall_u32!(lstat(
self.inner.as_ptr(),
self.stat_buf.as_mut_ptr() as *mut _
))
}
}
}
28 changes: 28 additions & 0 deletions monoio/src/fs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use std::{
};
use std::{io, path::Path};

#[cfg(unix)]
use super::{metadata::FileAttr, Metadata};
use crate::{
buf::{IoBuf, IoBufMut},
driver::{op::Op, shared_fd::SharedFd},
Expand Down Expand Up @@ -498,6 +500,32 @@ impl File {
self.fd.close().await;
Ok(())
}

/// Queries metadata about the underlying file.
///
/// # Examples
///
/// ```no_run
/// use monoio::fs::File;
///
/// #[monoio::main]
/// async fn main() -> std::io::Result<()> {
/// let mut f = File::open("foo.txt").await?;
/// let metadata = f.metadata().await?;
/// Ok(())
/// }
/// ```
#[cfg(unix)]
pub async fn metadata(&self) -> io::Result<Metadata> {
#[cfg(target_os = "linux")]
let flags = libc::AT_STATX_SYNC_AS_STAT | libc::AT_EMPTY_PATH;
#[cfg(target_os = "linux")]
let op = Op::statx_using_fd(&self.fd, flags)?;
#[cfg(target_os = "macos")]
let op = Op::statx_using_fd(&self.fd, true)?;

op.statx_result().await.map(FileAttr::from).map(Metadata)
}
}

#[cfg(unix)]
Expand Down
63 changes: 63 additions & 0 deletions monoio/src/fs/file_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::{fmt::Debug, os::unix::fs::FileTypeExt};

use libc::mode_t;

/// A structure representing a type of file with accessors for each file type.
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct FileType {
pub(crate) mode: mode_t,
}

#[cfg(unix)]
impl FileType {
/// Returns `true` if this file type is a directory.
pub fn is_dir(&self) -> bool {
self.is(libc::S_IFDIR)
}

/// Returns `true` if this file type is a regular file.
pub fn is_file(&self) -> bool {
self.is(libc::S_IFREG)
}

/// Returns `true` if this file type is a symbolic link.
pub fn is_symlink(&self) -> bool {
self.is(libc::S_IFLNK)
}

pub(crate) fn is(&self, mode: mode_t) -> bool {
self.masked() == mode
}

fn masked(&self) -> mode_t {
self.mode & libc::S_IFMT
}
}

impl FileTypeExt for FileType {
fn is_block_device(&self) -> bool {
self.is(libc::S_IFBLK)
}

fn is_char_device(&self) -> bool {
self.is(libc::S_IFCHR)
}

fn is_fifo(&self) -> bool {
self.is(libc::S_IFIFO)
}

fn is_socket(&self) -> bool {
self.is(libc::S_IFSOCK)
}
}

impl Debug for FileType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FileType")
.field("is_file", &self.is_file())
.field("is_dir", &self.is_dir())
.field("is_symlink", &self.is_symlink())
.finish_non_exhaustive()
}
}
Loading

0 comments on commit dbdb3d8

Please sign in to comment.