Skip to content

Commit abc1a16

Browse files
committed
feat(op): ✨ add MkDirAt opcode in io-uring
1 parent 2d653b2 commit abc1a16

File tree

8 files changed

+436
-0
lines changed

8 files changed

+436
-0
lines changed

monoio/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ async-cancel = []
6969
zero-copy = []
7070
# splice op(requires kernel 5.7+)
7171
splice = []
72+
# mkdirat2 op(requires kernel 5.15+)
73+
mkdirat = []
7274
# enable `async main` macros support
7375
macros = ["monoio-macros"]
7476
# allow waker to be sent across threads

monoio/src/driver/op.rs

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ mod write;
2222
#[cfg(unix)]
2323
mod statx;
2424

25+
#[cfg(all(unix, feature = "mkdirat"))]
26+
mod mkdir;
27+
2528
#[cfg(all(target_os = "linux", feature = "splice"))]
2629
mod splice;
2730

monoio/src/driver/op/mkdir.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use std::{ffi::CString, path::Path};
2+
3+
use libc::mode_t;
4+
5+
use super::{Op, OpAble};
6+
use crate::driver::util::cstr;
7+
8+
pub(crate) struct MkDir {
9+
path: CString,
10+
mode: mode_t,
11+
}
12+
13+
impl Op<MkDir> {
14+
pub(crate) fn mkdir<P: AsRef<Path>>(path: P, mode: mode_t) -> std::io::Result<Op<MkDir>> {
15+
let path = cstr(path.as_ref())?;
16+
Op::submit_with(MkDir { path, mode })
17+
}
18+
}
19+
20+
impl OpAble for MkDir {
21+
#[cfg(all(target_os = "linux", feature = "iouring"))]
22+
fn uring_op(&mut self) -> io_uring::squeue::Entry {
23+
use io_uring::{opcode, types};
24+
25+
opcode::MkDirAt::new(types::Fd(libc::AT_FDCWD), self.path.as_ptr())
26+
.mode(self.mode)
27+
.build()
28+
}
29+
30+
#[cfg(any(feature = "legacy", feature = "poll-io"))]
31+
#[inline]
32+
fn legacy_interest(&self) -> Option<(crate::driver::ready::Direction, usize)> {
33+
None
34+
}
35+
36+
#[cfg(all(any(feature = "legacy", feature = "poll-io"), unix))]
37+
fn legacy_call(&mut self) -> std::io::Result<u32> {
38+
use crate::syscall_u32;
39+
40+
syscall_u32!(mkdirat(libc::AT_FDCWD, self.path.as_ptr(), self.mode))
41+
}
42+
43+
#[cfg(all(any(feature = "legacy", feature = "poll-io"), windows))]
44+
fn legacy_call(&mut self) -> io::Result<u32> {
45+
unimplemented!()
46+
}
47+
}

monoio/src/fs/create_dir.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use std::{io, path::Path};
2+
3+
use super::DirBuilder;
4+
5+
/// Create a new directory at the target path
6+
///
7+
/// # Note
8+
///
9+
/// - This function require the provided path's parent are all existing.
10+
/// - To create a directory and all its missing parents at the same time, use the
11+
/// [`create_dir_all`] function.
12+
/// - Currently this function is supported on unix, windows is unimplement.
13+
///
14+
/// # Errors
15+
///
16+
/// This function will return an error in the following situations, but is not
17+
/// limited to just these cases:
18+
///
19+
/// * User lacks permissions to create directory at `path`.
20+
/// * A parent of the given path doesn't exist. (To create a directory and all its missing parents
21+
/// at the same time, use the [`create_dir_all`] function.)
22+
/// * `path` already exists.
23+
///
24+
/// # Examples
25+
///
26+
/// ```no_run
27+
/// use monoio::fs;
28+
///
29+
/// #[monoio::main]
30+
/// async fn main() -> std::io::Result<()> {
31+
/// fs::create_dir("/some/dir").await?;
32+
/// Ok(())
33+
/// }
34+
/// ```
35+
pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
36+
DirBuilder::new().create(path).await
37+
}
38+
39+
/// Recursively create a directory and all of its missing components
40+
///
41+
/// # Note
42+
///
43+
/// - Currently this function is supported on unix, windows is unimplement.
44+
///
45+
/// # Errors
46+
///
47+
/// Same with [`create_dir`]
48+
///
49+
/// # Examples
50+
///
51+
/// ```no_run
52+
/// use monoio::fs;
53+
///
54+
/// #[monoio::main]
55+
/// async fn main() -> std::io::Result<()> {
56+
/// fs::create_dir_all("/some/dir").await?;
57+
/// Ok(())
58+
/// }
59+
/// ```
60+
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
61+
DirBuilder::new().recursive(true).create(path).await
62+
}

monoio/src/fs/dir_builder/mod.rs

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
mod unix;
2+
3+
use std::{io, os::unix::fs::DirBuilderExt, path::Path};
4+
5+
#[cfg(unix)]
6+
use unix as sys;
7+
8+
/// A builder used to create directories in various manners.
9+
///
10+
/// This builder also supports platform-specific options.
11+
pub struct DirBuilder {
12+
recursive: bool,
13+
inner: sys::BuilderInner,
14+
}
15+
16+
impl DirBuilder {
17+
/// Creates a new set of options with default mode/security settings for all
18+
/// platforms and also non-recursive.
19+
///
20+
/// This an async version of [`std::fs::DirBuilder::new`]
21+
///
22+
/// # Examples
23+
///
24+
/// ```no_run
25+
/// use monoio::fs::DirBuilder;
26+
///
27+
/// let builder = DirBuilder::new();
28+
/// ```
29+
pub fn new() -> Self {
30+
Self {
31+
recursive: false,
32+
inner: sys::BuilderInner::new(),
33+
}
34+
}
35+
36+
/// Indicates that directories should be created recursively, creating all
37+
/// parent directories. Parents that do not exist are created with the same
38+
/// security and permissions settings.
39+
///
40+
/// This option defaults to `false`.
41+
///
42+
/// This an async version of [`std::fs::DirBuilder::recursive`]
43+
///
44+
/// # Examples
45+
///
46+
/// ```no_run
47+
/// use monoio::fs::DirBuilder;
48+
///
49+
/// let mut builder = DirBuilder::new();
50+
/// builder.recursive(true);
51+
/// ```
52+
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
53+
self.recursive = recursive;
54+
self
55+
}
56+
57+
/// Creates the specified directory with the options configured in this
58+
/// builder.
59+
///
60+
/// It is considered an error if the directory already exists unless
61+
/// recursive mode is enabled.
62+
///
63+
/// This is async version of [`std::fs::DirBuilder::create`] and use io-uring
64+
/// in support platform.
65+
///
66+
/// # Errors
67+
///
68+
/// An error will be returned under the following circumstances:
69+
///
70+
/// * Path already points to an existing file.
71+
/// * Path already points to an existing directory and the mode is non-recursive.
72+
/// * The calling process doesn't have permissions to create the directory or its missing
73+
/// parents.
74+
/// * Other I/O error occurred.
75+
///
76+
/// # Examples
77+
///
78+
/// ```no_run
79+
/// use monoio::fs::DirBuilder;
80+
///
81+
/// #[monoio::main]
82+
/// async fn main() -> std::io::Result<()> {
83+
/// DirBuilder::new()
84+
/// .recursive(true)
85+
/// .create("/some/dir")
86+
/// .await?;
87+
///
88+
/// Ok(())
89+
/// }
90+
/// ```
91+
pub async fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
92+
if self.recursive {
93+
self.create_dir_all(path.as_ref()).await
94+
} else {
95+
self.inner.mkdir(path.as_ref()).await
96+
}
97+
}
98+
99+
async fn create_dir_all(&self, path: &Path) -> io::Result<()> {
100+
if path == Path::new("") {
101+
return Ok(());
102+
}
103+
104+
let mut inexist_path = path;
105+
let mut need_create = vec![];
106+
107+
while match self.inner.mkdir(inexist_path).await {
108+
Ok(()) => false,
109+
Err(ref e) if e.kind() == io::ErrorKind::NotFound => true,
110+
Err(_) if is_dir(inexist_path).await => false,
111+
Err(e) => return Err(e),
112+
} {
113+
match inexist_path.parent() {
114+
Some(p) => {
115+
need_create.push(inexist_path);
116+
inexist_path = p;
117+
}
118+
None => {
119+
return Err(io::Error::new(
120+
io::ErrorKind::Other,
121+
"failed to create whole tree",
122+
))
123+
}
124+
}
125+
}
126+
127+
for p in need_create.into_iter().rev() {
128+
self.inner.mkdir(p).await?;
129+
}
130+
131+
Ok(())
132+
}
133+
}
134+
135+
impl Default for DirBuilder {
136+
fn default() -> Self {
137+
Self::new()
138+
}
139+
}
140+
141+
impl DirBuilderExt for DirBuilder {
142+
fn mode(&mut self, mode: u32) -> &mut Self {
143+
self.inner.set_mode(mode);
144+
self
145+
}
146+
}
147+
148+
// currently, will use the std version of metadata, will change to use the io-uring version
149+
// when the statx is merge
150+
async fn is_dir(path: &Path) -> bool {
151+
std::fs::metadata(path).is_ok_and(|metadata| metadata.is_dir())
152+
}

monoio/src/fs/dir_builder/unix.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use std::path::Path;
2+
3+
use libc::mode_t;
4+
5+
use crate::driver::op::Op;
6+
7+
pub(super) struct BuilderInner {
8+
mode: libc::mode_t,
9+
}
10+
11+
impl BuilderInner {
12+
pub(super) fn new() -> Self {
13+
Self { mode: 0o777 }
14+
}
15+
16+
pub(super) async fn mkdir(&self, path: &Path) -> std::io::Result<()> {
17+
Op::mkdir(path, self.mode)?.await.meta.result.map(|_| ())
18+
}
19+
20+
pub(super) fn set_mode(&mut self, mode: u32) {
21+
self.mode = mode as mode_t;
22+
}
23+
}

monoio/src/fs/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ use std::{io, path::Path};
55

66
pub use file::File;
77

8+
#[cfg(all(unix, feature = "mkdirat"))]
9+
mod dir_builder;
10+
#[cfg(all(unix, feature = "mkdirat"))]
11+
pub use dir_builder::DirBuilder;
12+
13+
#[cfg(all(unix, feature = "mkdirat"))]
14+
mod create_dir;
15+
#[cfg(all(unix, feature = "mkdirat"))]
16+
pub use create_dir::*;
17+
818
mod open_options;
919
pub use open_options::OpenOptions;
1020

0 commit comments

Comments
 (0)