Skip to content

Commit 0774c49

Browse files
committed
feat: support opcode::renameat
Signed-off-by: lzzzt <[email protected]>
1 parent 504a424 commit 0774c49

File tree

5 files changed

+169
-0
lines changed

5 files changed

+169
-0
lines changed

monoio/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ splice = []
7373
mkdirat = []
7474
# unlinkat op(requires kernel 5.11+)
7575
unlinkat = []
76+
# renameat op(requires kernel 5.11+)
77+
renameat = []
7678
# enable `async main` macros support
7779
macros = ["monoio-macros"]
7880
# allow waker to be sent across threads

monoio/src/driver/op.rs

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ mod mkdir;
2828
#[cfg(all(unix, feature = "unlinkat"))]
2929
mod unlink;
3030

31+
#[cfg(all(unix, feature = "renameat"))]
32+
mod rename;
33+
3134
#[cfg(all(target_os = "linux", feature = "splice"))]
3235
mod splice;
3336

monoio/src/driver/op/rename.rs

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

monoio/src/fs/mod.rs

+33
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,36 @@ pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
133133
Op::rmdir(path)?.await.meta.result?;
134134
Ok(())
135135
}
136+
137+
/// Rename a file or directory to a new name, replacing the original file if
138+
/// `to` already exists.
139+
///
140+
/// This will not work if the new name is on a different mount point.
141+
///
142+
/// This is async version of [std::fs::rename].
143+
///
144+
/// # Errors
145+
///
146+
/// This function will return an error in the following situations, but is not
147+
/// limited to just these cases:
148+
///
149+
/// * `from` does not exist.
150+
/// * The user lacks permissions to view contents.
151+
/// * `from` and `to` are on separate filesystems.
152+
///
153+
/// # Examples
154+
///
155+
/// ```no_run
156+
/// use monoio::fs;
157+
///
158+
/// #[monoio::main]
159+
/// async fn main() -> std::io::Result<()> {
160+
/// fs::rename("a.txt", "b.txt")?; // Rename a.txt to b.txt
161+
/// Ok(())
162+
/// }
163+
/// ```
164+
#[cfg(all(unix, feature = "renameat"))]
165+
pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
166+
Op::rename(from.as_ref(), to.as_ref())?.await.meta.result?;
167+
Ok(())
168+
}

monoio/tests/fs_rename.rs

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use std::{fs::Permissions, os::unix::fs::PermissionsExt};
2+
3+
#[monoio::test_all]
4+
async fn rename_file_in_the_same_directory() {
5+
let temp_dir = tempfile::tempdir().unwrap();
6+
let file = tempfile::NamedTempFile::new_in(temp_dir.path()).unwrap();
7+
8+
let old_file_path = file.path();
9+
let new_file_path = temp_dir.path().join("test-file");
10+
11+
let result = monoio::fs::rename(old_file_path, &new_file_path).await;
12+
assert!(result.is_ok());
13+
14+
assert!(new_file_path.exists());
15+
assert!(!old_file_path.exists());
16+
}
17+
18+
#[monoio::test_all]
19+
async fn rename_file_in_different_directory() {
20+
let temp_dir1 = tempfile::tempdir().unwrap();
21+
let temp_dir2 = tempfile::tempdir().unwrap();
22+
let file = tempfile::NamedTempFile::new_in(temp_dir1.path()).unwrap();
23+
24+
let old_file_path = file.path();
25+
let new_file_path = temp_dir2.path().join("test-file");
26+
27+
let result = monoio::fs::rename(old_file_path, &new_file_path).await;
28+
assert!(result.is_ok());
29+
30+
assert!(new_file_path.exists());
31+
assert!(!old_file_path.exists());
32+
}
33+
34+
#[monoio::test_all]
35+
async fn mv_file_in_different_directory() {
36+
let temp_dir1 = tempfile::tempdir().unwrap();
37+
let temp_dir2 = tempfile::tempdir().unwrap();
38+
let file = tempfile::NamedTempFile::new_in(temp_dir1.path()).unwrap();
39+
40+
let old_file_path = file.path();
41+
let old_file_name = old_file_path.file_name().unwrap();
42+
let new_file_path = temp_dir2.path().join(old_file_name);
43+
44+
let result = monoio::fs::rename(old_file_path, &new_file_path).await;
45+
assert!(result.is_ok());
46+
47+
assert!(new_file_path.exists());
48+
assert!(!old_file_path.exists());
49+
}
50+
51+
#[monoio::test_all]
52+
async fn rename_inexist_file() {
53+
let temp_dir = tempfile::tempdir().unwrap();
54+
55+
let old_file_path = temp_dir.path().join("inexist.txt");
56+
let new_file_path = temp_dir.path().join("renamed.txt");
57+
58+
let result = monoio::fs::rename(old_file_path, new_file_path).await;
59+
60+
assert!(result.is_err());
61+
}
62+
63+
#[monoio::test_all]
64+
async fn rename_file_without_permission() {
65+
let temp_dir = tempfile::tempdir().unwrap();
66+
let temp_file = tempfile::NamedTempFile::new_in(&temp_dir).unwrap();
67+
68+
std::fs::set_permissions(temp_dir.path(), Permissions::from_mode(0o0)).unwrap();
69+
70+
let old_file_path = temp_file.path();
71+
let new_file_path = temp_dir.path().join("test-file");
72+
73+
let result = monoio::fs::rename(old_file_path, &new_file_path).await;
74+
75+
assert!(result.is_err());
76+
}

0 commit comments

Comments
 (0)