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}