Skip to content

Commit

Permalink
feat(op): ✨ add MkDirAt opcode in io-uring
Browse files Browse the repository at this point in the history
  • Loading branch information
Lzzzzzt committed Aug 7, 2024
1 parent 5118537 commit 3a9fbc3
Show file tree
Hide file tree
Showing 8 changed files with 436 additions and 0 deletions.
2 changes: 2 additions & 0 deletions monoio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ async-cancel = []
zero-copy = []
# splice op(requires kernel 5.7+)
splice = []
# mkdirat2 op(requires kernel 5.15+)
mkdirat = []
# enable `async main` macros support
macros = ["monoio-macros"]
# allow waker to be sent across threads
Expand Down
3 changes: 3 additions & 0 deletions monoio/src/driver/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ mod write;
#[cfg(unix)]
mod statx;

#[cfg(all(unix, feature = "mkdirat"))]
mod mkdir;

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

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

use libc::mode_t;

use super::{Op, OpAble};
use crate::driver::util::cstr;

pub(crate) struct MkDir {
path: CString,
mode: mode_t,
}

impl Op<MkDir> {
pub(crate) fn mkdir<P: AsRef<Path>>(path: P, mode: mode_t) -> std::io::Result<Op<MkDir>> {
let path = cstr(path.as_ref())?;
Op::submit_with(MkDir { path, mode })
}
}

impl OpAble for MkDir {
#[cfg(all(target_os = "linux", feature = "iouring"))]
fn uring_op(&mut self) -> io_uring::squeue::Entry {
use io_uring::{opcode, types};

opcode::MkDirAt::new(types::Fd(libc::AT_FDCWD), self.path.as_ptr())
.mode(self.mode)
.build()
}

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

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

syscall_u32!(mkdirat(libc::AT_FDCWD, self.path.as_ptr(), self.mode))
}

#[cfg(all(any(feature = "legacy", feature = "poll-io"), windows))]
fn legacy_call(&mut self) -> io::Result<u32> {
unimplemented!()
}
}
62 changes: 62 additions & 0 deletions monoio/src/fs/create_dir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::{io, path::Path};

use super::DirBuilder;

/// Create a new directory at the target path
///
/// # Note
///
/// - This function require the provided path's parent are all existing.
/// - To create a directory and all its missing parents at the same time, use the
/// [`create_dir_all`] function.
/// - Currently this function is supported on unix, windows is unimplement.
///
/// # Errors
///
/// This function will return an error in the following situations, but is not
/// limited to just these cases:
///
/// * User lacks permissions to create directory at `path`.
/// * A parent of the given path doesn't exist. (To create a directory and all its missing parents
/// at the same time, use the [`create_dir_all`] function.)
/// * `path` already exists.
///
/// # Examples
///
/// ```no_run
/// use monoio::fs;
///
/// #[monoio::main]
/// async fn main() -> std::io::Result<()> {
/// fs::create_dir("/some/dir").await?;
/// Ok(())
/// }
/// ```
pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
DirBuilder::new().create(path).await
}

/// Recursively create a directory and all of its missing components
///
/// # Note
///
/// - Currently this function is supported on unix, windows is unimplement.
///
/// # Errors
///
/// Same with [`create_dir`]
///
/// # Examples
///
/// ```no_run
/// use monoio::fs;
///
/// #[monoio::main]
/// async fn main() -> std::io::Result<()> {
/// fs::create_dir_all("/some/dir").await?;
/// Ok(())
/// }
/// ```
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
DirBuilder::new().recursive(true).create(path).await
}
152 changes: 152 additions & 0 deletions monoio/src/fs/dir_builder/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
mod unix;

use std::{io, os::unix::fs::DirBuilderExt, path::Path};

#[cfg(unix)]
use unix as sys;

/// A builder used to create directories in various manners.
///
/// This builder also supports platform-specific options.
pub struct DirBuilder {
recursive: bool,
inner: sys::BuilderInner,
}

impl DirBuilder {
/// Creates a new set of options with default mode/security settings for all
/// platforms and also non-recursive.
///
/// This an async version of [`std::fs::DirBuilder::new`]
///
/// # Examples
///
/// ```no_run
/// use monoio::fs::DirBuilder;
///
/// let builder = DirBuilder::new();
/// ```
pub fn new() -> Self {
Self {
recursive: false,
inner: sys::BuilderInner::new(),
}
}

/// Indicates that directories should be created recursively, creating all
/// parent directories. Parents that do not exist are created with the same
/// security and permissions settings.
///
/// This option defaults to `false`.
///
/// This an async version of [`std::fs::DirBuilder::recursive`]
///
/// # Examples
///
/// ```no_run
/// use monoio::fs::DirBuilder;
///
/// let mut builder = DirBuilder::new();
/// builder.recursive(true);
/// ```
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
self.recursive = recursive;
self
}

/// Creates the specified directory with the options configured in this
/// builder.
///
/// It is considered an error if the directory already exists unless
/// recursive mode is enabled.
///
/// This is async version of [`std::fs::DirBuilder::create`] and use io-uring
/// in support platform.
///
/// # Errors
///
/// An error will be returned under the following circumstances:
///
/// * Path already points to an existing file.
/// * Path already points to an existing directory and the mode is non-recursive.
/// * The calling process doesn't have permissions to create the directory or its missing
/// parents.
/// * Other I/O error occurred.
///
/// # Examples
///
/// ```no_run
/// use monoio::fs::DirBuilder;
///
/// #[monoio::main]
/// async fn main() -> std::io::Result<()> {
/// DirBuilder::new()
/// .recursive(true)
/// .create("/some/dir")
/// .await?;
///
/// Ok(())
/// }
/// ```
pub async fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
if self.recursive {
self.create_dir_all(path.as_ref()).await
} else {
self.inner.mkdir(path.as_ref()).await
}
}

async fn create_dir_all(&self, path: &Path) -> io::Result<()> {
if path == Path::new("") {
return Ok(());
}

let mut inexist_path = path;
let mut need_create = vec![];

while match self.inner.mkdir(inexist_path).await {
Ok(()) => false,
Err(ref e) if e.kind() == io::ErrorKind::NotFound => true,
Err(_) if is_dir(inexist_path).await => false,
Err(e) => return Err(e),
} {
match inexist_path.parent() {
Some(p) => {
need_create.push(inexist_path);
inexist_path = p;
}
None => {
return Err(io::Error::new(
io::ErrorKind::Other,
"failed to create whole tree",
))
}
}
}

for p in need_create.into_iter().rev() {
self.inner.mkdir(p).await?;
}

Ok(())
}
}

impl Default for DirBuilder {
fn default() -> Self {
Self::new()
}
}

impl DirBuilderExt for DirBuilder {
fn mode(&mut self, mode: u32) -> &mut Self {
self.inner.set_mode(mode);
self
}
}

// currently, will use the std version of metadata, will change to use the io-uring version
// when the statx is merge
async fn is_dir(path: &Path) -> bool {
std::fs::metadata(path).is_ok_and(|metadata| metadata.is_dir())
}
23 changes: 23 additions & 0 deletions monoio/src/fs/dir_builder/unix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use std::path::Path;

use libc::mode_t;

use crate::driver::op::Op;

pub(super) struct BuilderInner {
mode: libc::mode_t,
}

impl BuilderInner {
pub(super) fn new() -> Self {
Self { mode: 0o777 }
}

pub(super) async fn mkdir(&self, path: &Path) -> std::io::Result<()> {
Op::mkdir(path, self.mode)?.await.meta.result.map(|_| ())
}

pub(super) fn set_mode(&mut self, mode: u32) {
self.mode = mode as mode_t;
}
}
10 changes: 10 additions & 0 deletions monoio/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ use std::{io, path::Path};

pub use file::File;

#[cfg(all(unix, feature = "mkdirat"))]
mod dir_builder;
#[cfg(all(unix, feature = "mkdirat"))]
pub use dir_builder::DirBuilder;

#[cfg(all(unix, feature = "mkdirat"))]
mod create_dir;
#[cfg(all(unix, feature = "mkdirat"))]
pub use create_dir::*;

mod open_options;
pub use open_options::OpenOptions;

Expand Down
Loading

0 comments on commit 3a9fbc3

Please sign in to comment.