monoio/fs/
mod.rs

1//! Filesystem manipulation operations.
2
3mod file;
4use std::{io, path::Path};
5
6pub use file::File;
7
8#[cfg(feature = "mkdirat")]
9mod dir_builder;
10#[cfg(feature = "mkdirat")]
11pub use dir_builder::DirBuilder;
12
13#[cfg(feature = "mkdirat")]
14mod create_dir;
15#[cfg(feature = "mkdirat")]
16pub use create_dir::*;
17
18#[cfg(all(unix, feature = "symlinkat"))]
19mod symlink;
20#[cfg(all(unix, feature = "symlinkat"))]
21pub use symlink::symlink;
22
23mod open_options;
24pub use open_options::OpenOptions;
25
26#[cfg(unix)]
27mod metadata;
28#[cfg(unix)]
29pub use metadata::{metadata, symlink_metadata, Metadata};
30
31#[cfg(unix)]
32mod file_type;
33#[cfg(unix)]
34pub use file_type::FileType;
35
36#[cfg(unix)]
37mod permissions;
38#[cfg(windows)]
39use std::os::windows::io::{AsRawHandle, FromRawHandle};
40
41#[cfg(unix)]
42pub use permissions::Permissions;
43
44use crate::buf::IoBuf;
45
46/// Executes a blocking operation asynchronously on a separate thread.
47///
48/// This function is designed to offload blocking I/O or CPU-bound tasks to a separate
49/// thread using `spawn_blocking`, which allows non-blocking async code to continue executing.
50///
51/// # Parameters
52///
53/// * `f`: A closure or function that performs the blocking operation. This function takes no
54///   arguments and returns an `io::Result<T>`. The closure must be `Send` and `'static` to ensure
55///   it can be safely run in a different thread.
56///
57/// # Returns
58///
59/// This function returns an `io::Result<T>`, where `T` is the type returned by the
60/// blocking operation. If the blocking task completes successfully, the result will
61/// be `Ok(T)`. If the background task fails, an `io::Error` with `io::ErrorKind::Other`
62/// will be returned.
63///
64/// # Errors
65///
66/// The function may return an `io::Error` in the following scenarios:
67/// - The blocking task returned an error, in which case the error is propagated.
68/// - The background task failed to complete due to an internal error, in which case an error with
69///   `io::ErrorKind::Other` is returned.
70#[cfg(all(feature = "sync", not(feature = "iouring")))]
71pub(crate) async fn asyncify<F, T>(f: F) -> io::Result<T>
72where
73    F: FnOnce() -> io::Result<T> + Send + 'static,
74    T: Send + 'static,
75{
76    use crate::spawn_blocking;
77
78    match spawn_blocking(f).await {
79        Ok(res) => res,
80        Err(_) => Err(io::Error::other("background task failed")),
81    }
82}
83
84/// A macro that generates the some Op-call functions.
85#[cfg(any(feature = "iouring", not(feature = "sync")))]
86#[macro_export]
87macro_rules! uring_op {
88    ($fn_name:ident<$trait_name:ident>($op_name: ident, $buf_name:ident $(, $pos:ident: $pos_type:ty)?)) => {
89        pub(crate) async fn $fn_name<T: $trait_name>(fd: SharedFd, $buf_name: T, $($pos: $pos_type)?) -> $crate::BufResult<usize, T> {
90            let op = $crate::driver::op::Op::$op_name(fd, $buf_name, $($pos)?).unwrap();
91            op.result().await
92        }
93    };
94}
95
96/// A macro that generates an asynchronous I/O operation function, offloading a blocking
97/// system call to a separate thread using the `asyncify` function.
98///
99/// This macro is intended to abstract the process of creating asynchronous functions for
100/// operations that involve reading or writing buffers, making it easier to create functions
101/// that perform system-level I/O asynchronously.
102#[cfg(all(feature = "sync", not(feature = "iouring")))]
103#[macro_export]
104macro_rules! asyncify_op {
105    (R, $fn_name:ident<$Trait: ident>($op:expr, $buf_ptr_expr:expr, $len_expr:expr $(, $extra_param:ident : $typ: ty)?)) => {
106        pub(crate) async fn $fn_name<T: $Trait>(
107            fd: SharedFd,
108            mut buf: T,
109            $($extra_param: $typ)?
110        ) -> $crate::BufResult<usize, T> {
111            #[cfg(unix)]
112            let fd = fd.as_raw_fd();
113            #[cfg(windows)]
114            let fd = fd.as_raw_handle() as _;
115            // Safety: Due to the trait `IoBuf*/IoVecBuf*` require the implemet of `*_ptr`
116            // should return the same address, it should be safe to convert it to `usize`
117            // and then convert back.
118            let buf_ptr = $buf_ptr_expr(&mut buf) as usize;
119            let len = $len_expr(&mut buf);
120
121            let res = $crate::fs::asyncify(move || $op(fd, buf_ptr as *mut _, len, $($extra_param)?))
122                .await
123                .map(|n| n.into_inner() as usize);
124
125            unsafe { buf.set_init(*res.as_ref().unwrap_or(&0)) };
126
127            (res, buf)
128        }
129    };
130    (W, $fn_name:ident<$Trait: ident>($op:expr, $buf_ptr_expr:expr, $len_expr:expr $(, $extra_param:ident : $typ: ty)?)) => {
131        pub(crate) async fn $fn_name<T: $Trait>(
132            fd: SharedFd,
133            mut buf: T,
134            $($extra_param: $typ)?
135        ) -> $crate::BufResult<usize, T> {
136            #[cfg(unix)]
137            let fd = fd.as_raw_fd();
138            #[cfg(windows)]
139            let fd = fd.as_raw_handle() as _;
140            // Safety: Due to the trait `IoBuf*/IoVecBuf*` require the implemet of `*_ptr`
141            // should return the same address, it should be safe to convert it to `usize`
142            // and then convert back.
143            let buf_ptr = $buf_ptr_expr(&mut buf) as usize;
144            let len = $len_expr(&mut buf);
145
146            let res = $crate::fs::asyncify(move || $op(fd, buf_ptr as *mut _, len, $($extra_param)?))
147                .await
148                .map(|n| n.into_inner() as usize);
149
150            // unsafe { buf.set_init(*res.as_ref().unwrap_or(&0)) };
151
152            (res, buf)
153        }
154    }
155}
156
157/// Read the entire contents of a file into a bytes vector.
158pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
159    use crate::buf::IoBufMut;
160
161    let file = File::open(path).await?;
162
163    #[cfg(windows)]
164    let size = {
165        let sys_file = std::mem::ManuallyDrop::new(unsafe {
166            std::fs::File::from_raw_handle(file.as_raw_handle())
167        });
168        sys_file.metadata()?.len() as usize
169    };
170
171    #[cfg(unix)]
172    let size = file.metadata().await?.len() as usize;
173
174    let (res, buf) = file
175        .read_exact_at(Vec::with_capacity(size).slice_mut(0..size), 0)
176        .await;
177    res?;
178    Ok(buf.into_inner())
179}
180
181/// Write a buffer as the entire contents of a file.
182pub async fn write<P: AsRef<Path>, C: IoBuf>(path: P, contents: C) -> (io::Result<()>, C) {
183    match File::create(path).await {
184        Ok(f) => f.write_all_at(contents, 0).await,
185        Err(e) => (Err(e), contents),
186    }
187}
188
189/// Removes a file from the filesystem.
190///
191/// Note that there is no
192/// guarantee that the file is immediately deleted (e.g., depending on
193/// platform, other open file descriptors may prevent immediate removal).
194///
195/// # Platform-specific behavior
196///
197/// This function is currently only implemented for Unix.
198///
199/// # Errors
200///
201/// This function will return an error in the following situations, but is not
202/// limited to just these cases:
203///
204/// * `path` points to a directory.
205/// * The file doesn't exist.
206/// * The user lacks permissions to remove the file.
207///
208/// # Examples
209///
210/// ```no_run
211/// #[monoio::main]
212/// async fn main() -> std::io::Result<()> {
213///     monoio::fs::remove_file("a.txt").await?;
214///     Ok(())
215/// }
216/// ```
217#[cfg(feature = "unlinkat")]
218pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
219    crate::driver::op::Op::unlink(path)?.await.meta.result?;
220    Ok(())
221}
222
223/// Removes an empty directory.
224///
225/// # Platform-specific behavior
226///
227/// This function is currently only implemented for Unix.
228///
229/// # Errors
230///
231/// This function will return an error in the following situations, but is not
232/// limited to just these cases:
233///
234/// * `path` doesn't exist.
235/// * `path` isn't a directory.
236/// * The user lacks permissions to remove the directory at the provided `path`.
237/// * The directory isn't empty.
238///
239/// # Examples
240///
241/// ```no_run
242/// #[monoio::main]
243/// async fn main() -> std::io::Result<()> {
244///     monoio::fs::remove_dir("/some/dir").await?;
245///     Ok(())
246/// }
247/// ```
248#[cfg(feature = "unlinkat")]
249pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
250    crate::driver::op::Op::rmdir(path)?.await.meta.result?;
251    Ok(())
252}
253
254/// Rename a file or directory to a new name, replacing the original file if
255/// `to` already exists.
256///
257/// This will not work if the new name is on a different mount point.
258///
259/// This is async version of [std::fs::rename].
260///
261/// # Errors
262///
263/// This function will return an error in the following situations, but is not
264/// limited to just these cases:
265///
266/// * `from` does not exist.
267/// * The user lacks permissions to view contents.
268/// * `from` and `to` are on separate filesystems.
269///
270/// # Examples
271///
272/// ```no_run
273/// #[monoio::main]
274/// async fn main() -> std::io::Result<()> {
275///     monoio::fs::rename("a.txt", "b.txt").await?; // Rename a.txt to b.txt
276///     Ok(())
277/// }
278/// ```
279#[cfg(feature = "renameat")]
280pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
281    crate::driver::op::Op::rename(from.as_ref(), to.as_ref())?
282        .await
283        .meta
284        .result?;
285    Ok(())
286}