Skip to content

Commit

Permalink
feat: unlink op, remove_file and remove_dir (#288)
Browse files Browse the repository at this point in the history
  • Loading branch information
NKID00 authored Aug 7, 2024
1 parent 2d34795 commit 504a424
Show file tree
Hide file tree
Showing 5 changed files with 171 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 @@ -71,6 +71,8 @@ zero-copy = []
splice = []
# mkdirat2 op(requires kernel 5.15+)
mkdirat = []
# unlinkat op(requires kernel 5.11+)
unlinkat = []
# 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 @@ -25,6 +25,9 @@ mod statx;
#[cfg(all(unix, feature = "mkdirat"))]
mod mkdir;

#[cfg(all(unix, feature = "unlinkat"))]
mod unlink;

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

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

#[cfg(all(target_os = "linux", feature = "iouring"))]
use io_uring::{opcode, squeue::Entry, types::Fd};
#[cfg(all(target_os = "linux", feature = "iouring"))]
use libc::{AT_FDCWD, AT_REMOVEDIR};

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

pub(crate) struct Unlink {
path: CString,
remove_dir: bool,
}

impl Op<Unlink> {
pub(crate) fn unlink<P: AsRef<Path>>(path: P) -> io::Result<Op<Unlink>> {
let path = cstr(path.as_ref())?;
Op::submit_with(Unlink {
path,
remove_dir: false,
})
}

pub(crate) fn rmdir<P: AsRef<Path>>(path: P) -> io::Result<Op<Unlink>> {
let path = cstr(path.as_ref())?;
Op::submit_with(Unlink {
path,
remove_dir: true,
})
}
}

impl OpAble for Unlink {
#[cfg(all(target_os = "linux", feature = "iouring"))]
fn uring_op(&mut self) -> Entry {
opcode::UnlinkAt::new(Fd(AT_FDCWD), self.path.as_c_str().as_ptr())
.flags(if self.remove_dir { AT_REMOVEDIR } else { 0 })
.build()
}

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

#[cfg(any(feature = "legacy", feature = "poll-io"))]
fn legacy_call(&mut self) -> io::Result<u32> {
if self.remove_dir {
syscall_u32!(rmdir(self.path.as_c_str().as_ptr()))
} else {
syscall_u32!(unlink(self.path.as_c_str().as_ptr()))
}
}
}
71 changes: 71 additions & 0 deletions monoio/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ mod permissions;
pub use permissions::Permissions;

use crate::buf::IoBuf;
#[cfg(all(unix, feature = "unlinkat"))]
use crate::driver::op::Op;

/// Read the entire contents of a file into a bytes vector.
#[cfg(unix)]
Expand Down Expand Up @@ -62,3 +64,72 @@ pub async fn write<P: AsRef<Path>, C: IoBuf>(path: P, contents: C) -> (io::Resul
};
file.write_all_at(contents, 0).await
}

/// Removes a file from the filesystem.
///
/// Note that there is no
/// guarantee that the file is immediately deleted (e.g., depending on
/// platform, other open file descriptors may prevent immediate removal).
///
/// # Platform-specific behavior
///
/// This function is currently only implemented for Unix.
///
/// # Errors
///
/// This function will return an error in the following situations, but is not
/// limited to just these cases:
///
/// * `path` points to a directory.
/// * The file doesn't exist.
/// * The user lacks permissions to remove the file.
///
/// # Examples
///
/// ```no_run
/// use monoio::fs::File;
///
/// #[monoio::main]
/// async fn main() -> std::io::Result<()> {
/// fs::remove_file("a.txt").await?;
/// Ok(())

Check failure on line 95 in monoio/src/fs/mod.rs

View workflow job for this annotation

GitHub Actions / Run cargo coverage

failed to resolve: use of undeclared crate or module `fs`
/// }
/// ```
#[cfg(all(unix, feature = "unlinkat"))]
pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
Op::unlink(path)?.await.meta.result?;
Ok(())
}

/// Removes an empty directory.
///
/// # Platform-specific behavior
///
/// This function is currently only implemented for Unix.
///
/// # Errors
///
/// This function will return an error in the following situations, but is not
/// limited to just these cases:
///
/// * `path` doesn't exist.
/// * `path` isn't a directory.
/// * The user lacks permissions to remove the directory at the provided `path`.
/// * The directory isn't empty.
///
/// # Examples
///
/// ```no_run
/// use monoio::fs::File;
///
/// #[monoio::main]
/// async fn main() -> std::io::Result<()> {
/// fs::remove_dir("/some/dir").await?;
/// Ok(())

Check failure on line 128 in monoio/src/fs/mod.rs

View workflow job for this annotation

GitHub Actions / Run cargo coverage

failed to resolve: use of undeclared crate or module `fs`
/// }
/// ```
#[cfg(all(unix, feature = "unlinkat"))]
pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
Op::rmdir(path)?.await.meta.result?;
Ok(())
}
38 changes: 38 additions & 0 deletions monoio/tests/fs_unlink.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#![cfg(all(unix, feature = "unlinkat", feature = "mkdirat"))]

use std::{io, path::PathBuf};

use monoio::fs::{self, File};
use tempfile::tempdir;

async fn create_file(path: &PathBuf) -> io::Result<()> {
let file = File::create(path).await?;
file.close().await?;
Ok(())
}

#[monoio::test_all]
async fn remove_file() {
let dir = tempdir().unwrap();
let target = dir.path().join("test");

create_file(&target).await.unwrap();
fs::remove_file(&target).await.unwrap();
assert!(File::open(&target).await.is_err());
assert!(fs::remove_file(&target).await.is_err());
}

#[monoio::test_all]
async fn remove_dir() {
let dir = tempdir().unwrap();
let target = dir.path().join("test");

fs::create_dir(&target).await.unwrap();
let path = target.join("file");
create_file(&path).await.unwrap();
assert!(fs::remove_dir(&target).await.is_err()); // dir is not empty
fs::remove_file(&path).await.unwrap();
fs::remove_dir(&target).await.unwrap();
assert!(create_file(&path).await.is_err()); // dir has been removed
assert!(fs::remove_dir(&target).await.is_err());
}

0 comments on commit 504a424

Please sign in to comment.