Skip to main content

nuclei/fs/
ops.rs

1//! The file system's calls by path: reading and writing whole files, metadata,
2//! directories, links, renames and copies, and [`OpenOptions`] and
3//! [`DirBuilder`]. Each runs on the blocking pool, as a regular file's calls
4//! do; the task waits for its end without holding its shard.
5
6use std::fs::{Metadata, Permissions};
7use std::io;
8use std::path::{Path, PathBuf};
9
10use crate::io::Handle;
11pub(crate) use crate::rt::blocking::call as blocking;
12
13/// The whole file at `path`.
14pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
15    let path = path.as_ref().to_path_buf();
16    blocking(move || std::fs::read(path)).await
17}
18
19/// The whole file at `path`, which must be UTF-8.
20pub async fn read_to_string(path: impl AsRef<Path>) -> io::Result<String> {
21    let path = path.as_ref().to_path_buf();
22    blocking(move || std::fs::read_to_string(path)).await
23}
24
25/// Writes `contents` as the whole file at `path`, creating it or cutting it
26/// down first.
27pub async fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> io::Result<()> {
28    let (path, contents) = (path.as_ref().to_path_buf(), contents.as_ref().to_vec());
29    blocking(move || std::fs::write(path, contents)).await
30}
31
32/// The metadata of what `path` names, through links.
33pub async fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
34    let path = path.as_ref().to_path_buf();
35    blocking(move || std::fs::metadata(path)).await
36}
37
38/// The metadata of what `path` names, a link itself rather than its target.
39pub async fn symlink_metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
40    let path = path.as_ref().to_path_buf();
41    blocking(move || std::fs::symlink_metadata(path)).await
42}
43
44/// Whether `path` names something: `Ok(false)` when it does not, an error
45/// when that cannot be told (no permission, for one).
46pub async fn try_exists(path: impl AsRef<Path>) -> io::Result<bool> {
47    let path = path.as_ref().to_path_buf();
48    blocking(move || path.try_exists()).await
49}
50
51/// Creates the directory `path`; its parent must exist.
52pub async fn create_dir(path: impl AsRef<Path>) -> io::Result<()> {
53    let path = path.as_ref().to_path_buf();
54    blocking(move || std::fs::create_dir(path)).await
55}
56
57/// Creates the directory `path` and every missing parent.
58pub async fn create_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
59    let path = path.as_ref().to_path_buf();
60    blocking(move || std::fs::create_dir_all(path)).await
61}
62
63/// Removes the file at `path`.
64pub async fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
65    let path = path.as_ref().to_path_buf();
66    blocking(move || std::fs::remove_file(path)).await
67}
68
69/// Removes the empty directory at `path`.
70pub async fn remove_dir(path: impl AsRef<Path>) -> io::Result<()> {
71    let path = path.as_ref().to_path_buf();
72    blocking(move || std::fs::remove_dir(path)).await
73}
74
75/// Removes the directory at `path` and everything in it.
76pub async fn remove_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
77    let path = path.as_ref().to_path_buf();
78    blocking(move || std::fs::remove_dir_all(path)).await
79}
80
81/// Renames `from` to `to`, replacing `to` if it is a file.
82pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
83    let (from, to) = (from.as_ref().to_path_buf(), to.as_ref().to_path_buf());
84    blocking(move || std::fs::rename(from, to)).await
85}
86
87/// Copies the file `from` to `to`, with its permissions; answers the bytes
88/// copied.
89pub async fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<u64> {
90    let (from, to) = (from.as_ref().to_path_buf(), to.as_ref().to_path_buf());
91    blocking(move || std::fs::copy(from, to)).await
92}
93
94/// Makes `link` a new name of the file `original` (a hard link).
95pub async fn hard_link(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
96    let (original, link) = (original.as_ref().to_path_buf(), link.as_ref().to_path_buf());
97    blocking(move || std::fs::hard_link(original, link)).await
98}
99
100/// Makes `link` a symbolic link to `original`.
101#[cfg(unix)]
102pub async fn symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
103    let (original, link) = (original.as_ref().to_path_buf(), link.as_ref().to_path_buf());
104    blocking(move || std::os::unix::fs::symlink(original, link)).await
105}
106
107/// Makes `link` a symbolic link to the file `original`.
108#[cfg(windows)]
109pub async fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
110    let (original, link) = (original.as_ref().to_path_buf(), link.as_ref().to_path_buf());
111    blocking(move || std::os::windows::fs::symlink_file(original, link)).await
112}
113
114/// Makes `link` a symbolic link to the directory `original`.
115#[cfg(windows)]
116pub async fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
117    let (original, link) = (original.as_ref().to_path_buf(), link.as_ref().to_path_buf());
118    blocking(move || std::os::windows::fs::symlink_dir(original, link)).await
119}
120
121/// Where the symbolic link `path` points.
122pub async fn read_link(path: impl AsRef<Path>) -> io::Result<PathBuf> {
123    let path = path.as_ref().to_path_buf();
124    blocking(move || std::fs::read_link(path)).await
125}
126
127/// The absolute path of `path`, every link and `..` resolved.
128pub async fn canonicalize(path: impl AsRef<Path>) -> io::Result<PathBuf> {
129    let path = path.as_ref().to_path_buf();
130    blocking(move || std::fs::canonicalize(path)).await
131}
132
133/// Sets the permissions of what `path` names.
134pub async fn set_permissions(path: impl AsRef<Path>, perm: Permissions) -> io::Result<()> {
135    let path = path.as_ref().to_path_buf();
136    blocking(move || std::fs::set_permissions(path, perm)).await
137}
138
139/// How to open a file: the options of std's `OpenOptions`, opened on the
140/// blocking pool into a [`Handle`].
141#[derive(Clone, Debug)]
142pub struct OpenOptions(std::fs::OpenOptions);
143
144impl Default for OpenOptions {
145    fn default() -> Self {
146        Self::new()
147    }
148}
149
150impl OpenOptions {
151    /// No option set: neither read nor write.
152    pub fn new() -> Self {
153        Self(std::fs::OpenOptions::new())
154    }
155
156    /// Opens for reading.
157    pub fn read(&mut self, read: bool) -> &mut Self {
158        self.0.read(read);
159        self
160    }
161
162    /// Opens for writing.
163    pub fn write(&mut self, write: bool) -> &mut Self {
164        self.0.write(write);
165        self
166    }
167
168    /// Opens for writing at the end, whatever the cursor.
169    pub fn append(&mut self, append: bool) -> &mut Self {
170        self.0.append(append);
171        self
172    }
173
174    /// Cuts an existing file down to nothing when it opens.
175    pub fn truncate(&mut self, truncate: bool) -> &mut Self {
176        self.0.truncate(truncate);
177        self
178    }
179
180    /// Creates the file if it does not exist.
181    pub fn create(&mut self, create: bool) -> &mut Self {
182        self.0.create(create);
183        self
184    }
185
186    /// Creates the file, failing if it exists.
187    pub fn create_new(&mut self, create_new: bool) -> &mut Self {
188        self.0.create_new(create_new);
189        self
190    }
191
192    /// The mode bits a created file gets (before the umask).
193    #[cfg(unix)]
194    pub fn mode(&mut self, mode: u32) -> &mut Self {
195        std::os::unix::fs::OpenOptionsExt::mode(&mut self.0, mode);
196        self
197    }
198
199    /// Flags passed to `open` beside the ones the options set.
200    #[cfg(unix)]
201    pub fn custom_flags(&mut self, flags: i32) -> &mut Self {
202        std::os::unix::fs::OpenOptionsExt::custom_flags(&mut self.0, flags);
203        self
204    }
205
206    /// The access mode `CreateFileW` gets, over read and write.
207    #[cfg(windows)]
208    pub fn access_mode(&mut self, access: u32) -> &mut Self {
209        std::os::windows::fs::OpenOptionsExt::access_mode(&mut self.0, access);
210        self
211    }
212
213    /// The share mode `CreateFileW` gets.
214    #[cfg(windows)]
215    pub fn share_mode(&mut self, share: u32) -> &mut Self {
216        std::os::windows::fs::OpenOptionsExt::share_mode(&mut self.0, share);
217        self
218    }
219
220    /// Flags `CreateFileW` gets beside the ones the options set.
221    #[cfg(windows)]
222    pub fn custom_flags(&mut self, flags: u32) -> &mut Self {
223        std::os::windows::fs::OpenOptionsExt::custom_flags(&mut self.0, flags);
224        self
225    }
226
227    /// The attributes a created file gets.
228    #[cfg(windows)]
229    pub fn attributes(&mut self, attributes: u32) -> &mut Self {
230        std::os::windows::fs::OpenOptionsExt::attributes(&mut self.0, attributes);
231        self
232    }
233
234    /// The security quality of service flags `CreateFileW` gets.
235    #[cfg(windows)]
236    pub fn security_qos_flags(&mut self, flags: u32) -> &mut Self {
237        std::os::windows::fs::OpenOptionsExt::security_qos_flags(&mut self.0, flags);
238        self
239    }
240
241    /// Opens the file at `path` with these options.
242    pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<Handle<std::fs::File>> {
243        let (options, path) = (self.0.clone(), path.as_ref().to_path_buf());
244        let file = blocking(move || options.open(path)).await?;
245        Handle::new(file)
246    }
247}
248
249impl From<std::fs::OpenOptions> for OpenOptions {
250    fn from(options: std::fs::OpenOptions) -> Self {
251        Self(options)
252    }
253}
254
255/// How to create a directory: recursively or not, and with which mode.
256#[derive(Debug, Default)]
257pub struct DirBuilder {
258    recursive: bool,
259    #[cfg(unix)]
260    mode: Option<u32>,
261}
262
263impl DirBuilder {
264    /// Creates one directory, with the default mode.
265    pub fn new() -> Self {
266        Self::default()
267    }
268
269    /// Creates every missing parent too.
270    pub fn recursive(&mut self, recursive: bool) -> &mut Self {
271        self.recursive = recursive;
272        self
273    }
274
275    /// The mode bits the directories get (before the umask).
276    #[cfg(unix)]
277    pub fn mode(&mut self, mode: u32) -> &mut Self {
278        self.mode = Some(mode);
279        self
280    }
281
282    /// Creates the directory `path`.
283    pub async fn create(&self, path: impl AsRef<Path>) -> io::Result<()> {
284        let mut builder = std::fs::DirBuilder::new();
285        builder.recursive(self.recursive);
286        #[cfg(unix)]
287        if let Some(mode) = self.mode {
288            std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode);
289        }
290        let path = path.as_ref().to_path_buf();
291        blocking(move || builder.create(path)).await
292    }
293}