Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(op, fs): ✨ add MkDirAt opcode in io-uring and create_dir(_all) #270

Merged
merged 1 commit into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading