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: unlink op, remove_file and remove_dir #288

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 @@ -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(())
/// }
/// ```
#[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(())
/// }
/// ```
#[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());
}
Loading