1 | //! Async filesystem primitives. |
2 | //! |
3 | //! This crate is an async version of [`std::fs`]. |
4 | //! |
5 | //! # Implementation |
6 | //! |
7 | //! This crate uses [`blocking`] to offload blocking I/O onto a thread pool. |
8 | //! |
9 | //! [`blocking`]: https://docs.rs/blocking |
10 | //! |
11 | //! # Examples |
12 | //! |
13 | //! Create a new file and write some bytes to it: |
14 | //! |
15 | //! ```no_run |
16 | //! use async_fs::File; |
17 | //! use futures_lite::io::AsyncWriteExt; |
18 | //! |
19 | //! # futures_lite::future::block_on(async { |
20 | //! let mut file = File::create("a.txt" ).await?; |
21 | //! file.write_all(b"Hello, world!" ).await?; |
22 | //! file.flush().await?; |
23 | //! # std::io::Result::Ok(()) }); |
24 | //! ``` |
25 | |
26 | #![forbid (unsafe_code)] |
27 | #![warn (missing_docs, missing_debug_implementations, rust_2018_idioms)] |
28 | #![doc ( |
29 | html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" |
30 | )] |
31 | #![doc ( |
32 | html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" |
33 | )] |
34 | |
35 | use std::ffi::OsString; |
36 | use std::fmt; |
37 | use std::future::Future; |
38 | use std::io::{self, SeekFrom}; |
39 | use std::path::{Path, PathBuf}; |
40 | use std::pin::Pin; |
41 | use std::sync::Arc; |
42 | use std::task::{Context, Poll}; |
43 | |
44 | #[cfg (unix)] |
45 | use std::os::unix::fs::{DirEntryExt as _, OpenOptionsExt as _}; |
46 | |
47 | #[cfg (windows)] |
48 | use std::os::windows::fs::OpenOptionsExt as _; |
49 | |
50 | use async_lock::Mutex; |
51 | use blocking::{unblock, Unblock}; |
52 | use futures_lite::future::FutureExt; |
53 | use futures_lite::io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt}; |
54 | use futures_lite::ready; |
55 | use futures_lite::stream::Stream; |
56 | |
57 | #[doc (no_inline)] |
58 | pub use std::fs::{FileType, Metadata, Permissions}; |
59 | |
60 | /// Returns the canonical form of a path. |
61 | /// |
62 | /// The returned path is in absolute form with all intermediate components normalized and symbolic |
63 | /// links resolved. |
64 | /// |
65 | /// # Errors |
66 | /// |
67 | /// An error will be returned in the following situations: |
68 | /// |
69 | /// * `path` does not point to an existing file or directory. |
70 | /// * A non-final component in `path` is not a directory. |
71 | /// * Some other I/O error occurred. |
72 | /// |
73 | /// # Examples |
74 | /// |
75 | /// ```no_run |
76 | /// # futures_lite::future::block_on(async { |
77 | /// let path = async_fs::canonicalize("." ).await?; |
78 | /// # std::io::Result::Ok(()) }); |
79 | /// ``` |
80 | pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { |
81 | let path: PathBuf = path.as_ref().to_owned(); |
82 | unblock(move || std::fs::canonicalize(path)).await |
83 | } |
84 | |
85 | /// Copies a file to a new location. |
86 | /// |
87 | /// On success, the total number of bytes copied is returned and equals the length of the `dst` |
88 | /// file after this operation. |
89 | /// |
90 | /// The old contents of `dst` will be overwritten. If `src` and `dst` both point to the same |
91 | /// file, then the file will likely get truncated as a result of this operation. |
92 | /// |
93 | /// If you're working with open [`File`]s and want to copy contents through those types, use |
94 | /// [`futures_lite::io::copy()`] instead. |
95 | /// |
96 | /// # Errors |
97 | /// |
98 | /// An error will be returned in the following situations: |
99 | /// |
100 | /// * `src` does not point to an existing file. |
101 | /// * The current process lacks permissions to read `src` or write `dst`. |
102 | /// * Some other I/O error occurred. |
103 | /// |
104 | /// # Examples |
105 | /// |
106 | /// ```no_run |
107 | /// # futures_lite::future::block_on(async { |
108 | /// let num_bytes = async_fs::copy("a.txt" , "b.txt" ).await?; |
109 | /// # std::io::Result::Ok(()) }); |
110 | /// ``` |
111 | pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<u64> { |
112 | let src: PathBuf = src.as_ref().to_owned(); |
113 | let dst: PathBuf = dst.as_ref().to_owned(); |
114 | unblock(move || std::fs::copy(&src, &dst)).await |
115 | } |
116 | |
117 | /// Creates a new, empty directory at the provided path |
118 | /// |
119 | /// # Platform-specific behavior |
120 | /// |
121 | /// This function currently corresponds to the `mkdir` function on Unix |
122 | /// and the `CreateDirectory` function on Windows. |
123 | /// Note that, this [may change in the future][changes]. |
124 | /// |
125 | /// [changes]: io#platform-specific-behavior |
126 | /// |
127 | /// **NOTE**: If a parent of the given path doesn't exist, this function will |
128 | /// return an error. To create a directory and all its missing parents at the |
129 | /// same time, use the [`create_dir_all`] function. |
130 | /// |
131 | /// # Errors |
132 | /// |
133 | /// This function will return an error in the following situations, but is not |
134 | /// limited to just these cases: |
135 | /// |
136 | /// * User lacks permissions to create directory at `path`. |
137 | /// * A parent of the given path doesn't exist. (To create a directory and all |
138 | /// its missing parents at the same time, use the [`create_dir_all`] |
139 | /// function.) |
140 | /// * `path` already exists. |
141 | /// |
142 | /// # Examples |
143 | /// |
144 | /// ```no_run |
145 | /// use std::fs; |
146 | /// |
147 | /// fn main() -> std::io::Result<()> { |
148 | /// fs::create_dir("/some/dir" )?; |
149 | /// Ok(()) |
150 | /// } |
151 | /// ``` |
152 | pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { |
153 | let path: PathBuf = path.as_ref().to_owned(); |
154 | unblock(move || std::fs::create_dir(path)).await |
155 | } |
156 | |
157 | /// Recursively create a directory and all of its parent components if they |
158 | /// are missing. |
159 | /// |
160 | /// # Platform-specific behavior |
161 | /// |
162 | /// This function currently corresponds to the `mkdir` function on Unix |
163 | /// and the `CreateDirectory` function on Windows. |
164 | /// Note that, this [may change in the future][changes]. |
165 | /// |
166 | /// [changes]: io#platform-specific-behavior |
167 | /// |
168 | /// # Errors |
169 | /// |
170 | /// This function will return an error in the following situations, but is not |
171 | /// limited to just these cases: |
172 | /// |
173 | /// * If any directory in the path specified by `path` |
174 | /// does not already exist and it could not be created otherwise. The specific |
175 | /// error conditions for when a directory is being created (after it is |
176 | /// determined to not exist) are outlined by [`fs::create_dir`]. |
177 | /// |
178 | /// Notable exception is made for situations where any of the directories |
179 | /// specified in the `path` could not be created as it was being created concurrently. |
180 | /// Such cases are considered to be successful. That is, calling `create_dir_all` |
181 | /// concurrently from multiple threads or processes is guaranteed not to fail |
182 | /// due to a race condition with itself. |
183 | /// |
184 | /// [`fs::create_dir`]: create_dir |
185 | /// |
186 | /// # Examples |
187 | /// |
188 | /// ```no_run |
189 | /// use std::fs; |
190 | /// |
191 | /// fn main() -> std::io::Result<()> { |
192 | /// fs::create_dir_all("/some/dir" )?; |
193 | /// Ok(()) |
194 | /// } |
195 | /// ``` |
196 | pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { |
197 | let path: PathBuf = path.as_ref().to_owned(); |
198 | unblock(move || std::fs::create_dir_all(path)).await |
199 | } |
200 | |
201 | /// Creates a hard link on the filesystem. |
202 | /// |
203 | /// The `dst` path will be a link pointing to the `src` path. Note that operating systems often |
204 | /// require these two paths to be located on the same filesystem. |
205 | /// |
206 | /// # Errors |
207 | /// |
208 | /// An error will be returned in the following situations: |
209 | /// |
210 | /// * `src` does not point to an existing file. |
211 | /// * Some other I/O error occurred. |
212 | /// |
213 | /// # Examples |
214 | /// |
215 | /// ```no_run |
216 | /// # futures_lite::future::block_on(async { |
217 | /// async_fs::hard_link("a.txt" , "b.txt" ).await?; |
218 | /// # std::io::Result::Ok(()) }); |
219 | /// ``` |
220 | pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { |
221 | let src: PathBuf = src.as_ref().to_owned(); |
222 | let dst: PathBuf = dst.as_ref().to_owned(); |
223 | unblock(move || std::fs::hard_link(&src, &dst)).await |
224 | } |
225 | |
226 | /// Reads metadata for a path. |
227 | /// |
228 | /// This function will traverse symbolic links to read metadata for the target file or directory. |
229 | /// If you want to read metadata without following symbolic links, use [`symlink_metadata()`] |
230 | /// instead. |
231 | /// |
232 | /// # Errors |
233 | /// |
234 | /// An error will be returned in the following situations: |
235 | /// |
236 | /// * `path` does not point to an existing file or directory. |
237 | /// * The current process lacks permissions to read metadata for the path. |
238 | /// * Some other I/O error occurred. |
239 | /// |
240 | /// # Examples |
241 | /// |
242 | /// ```no_run |
243 | /// # futures_lite::future::block_on(async { |
244 | /// let perm = async_fs::metadata("a.txt" ).await?.permissions(); |
245 | /// # std::io::Result::Ok(()) }); |
246 | /// ``` |
247 | pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { |
248 | let path: PathBuf = path.as_ref().to_owned(); |
249 | unblock(move || std::fs::metadata(path)).await |
250 | } |
251 | |
252 | /// Reads the entire contents of a file as raw bytes. |
253 | /// |
254 | /// This is a convenience function for reading entire files. It pre-allocates a buffer based on the |
255 | /// file size when available, so it is typically faster than manually opening a file and reading |
256 | /// from it. |
257 | /// |
258 | /// If you want to read the contents as a string, use [`read_to_string()`] instead. |
259 | /// |
260 | /// # Errors |
261 | /// |
262 | /// An error will be returned in the following situations: |
263 | /// |
264 | /// * `path` does not point to an existing file. |
265 | /// * The current process lacks permissions to read the file. |
266 | /// * Some other I/O error occurred. |
267 | /// |
268 | /// # Examples |
269 | /// |
270 | /// ```no_run |
271 | /// # futures_lite::future::block_on(async { |
272 | /// let contents = async_fs::read("a.txt" ).await?; |
273 | /// # std::io::Result::Ok(()) }); |
274 | /// ``` |
275 | pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> { |
276 | let path: PathBuf = path.as_ref().to_owned(); |
277 | unblock(move || std::fs::read(path)).await |
278 | } |
279 | |
280 | /// Returns a stream of entries in a directory. |
281 | /// |
282 | /// The stream yields items of type [`io::Result`]`<`[`DirEntry`]`>`. Note that I/O errors can |
283 | /// occur while reading from the stream. |
284 | /// |
285 | /// # Errors |
286 | /// |
287 | /// An error will be returned in the following situations: |
288 | /// |
289 | /// * `path` does not point to an existing directory. |
290 | /// * The current process lacks permissions to read the contents of the directory. |
291 | /// * Some other I/O error occurred. |
292 | /// |
293 | /// # Examples |
294 | /// |
295 | /// ```no_run |
296 | /// # futures_lite::future::block_on(async { |
297 | /// use futures_lite::stream::StreamExt; |
298 | /// |
299 | /// let mut entries = async_fs::read_dir("." ).await?; |
300 | /// |
301 | /// while let Some(entry) = entries.try_next().await? { |
302 | /// println!("{}" , entry.file_name().to_string_lossy()); |
303 | /// } |
304 | /// # std::io::Result::Ok(()) }); |
305 | /// ``` |
306 | pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { |
307 | let path: PathBuf = path.as_ref().to_owned(); |
308 | unblock(move || std::fs::read_dir(path).map(|inner: ReadDir| ReadDir(State::Idle(Some(inner))))).await |
309 | } |
310 | |
311 | /// A stream of entries in a directory. |
312 | /// |
313 | /// This stream is returned by [`read_dir()`] and yields items of type |
314 | /// [`io::Result`]`<`[`DirEntry`]`>`. Each [`DirEntry`] can then retrieve information like entry's |
315 | /// path or metadata. |
316 | pub struct ReadDir(State); |
317 | |
318 | /// The state of an asynchronous `ReadDir`. |
319 | /// |
320 | /// The `ReadDir` can be either idle or busy performing an asynchronous operation. |
321 | enum State { |
322 | Idle(Option<std::fs::ReadDir>), |
323 | Busy(blocking::Task<(std::fs::ReadDir, Option<io::Result<std::fs::DirEntry>>)>), |
324 | } |
325 | |
326 | impl fmt::Debug for ReadDir { |
327 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
328 | f.debug_struct(name:"ReadDir" ).finish() |
329 | } |
330 | } |
331 | |
332 | impl Stream for ReadDir { |
333 | type Item = io::Result<DirEntry>; |
334 | |
335 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { |
336 | loop { |
337 | match &mut self.0 { |
338 | State::Idle(opt) => { |
339 | let mut inner = opt.take().unwrap(); |
340 | |
341 | // Start the operation asynchronously. |
342 | self.0 = State::Busy(unblock(move || { |
343 | let next = inner.next(); |
344 | (inner, next) |
345 | })); |
346 | } |
347 | // Poll the asynchronous operation the file is currently blocked on. |
348 | State::Busy(task) => { |
349 | let (inner, opt) = ready!(task.poll(cx)); |
350 | self.0 = State::Idle(Some(inner)); |
351 | return Poll::Ready(opt.map(|res| res.map(|inner| DirEntry(Arc::new(inner))))); |
352 | } |
353 | } |
354 | } |
355 | } |
356 | } |
357 | |
358 | /// An entry in a directory. |
359 | /// |
360 | /// A stream of entries in a directory is returned by [`read_dir()`]. |
361 | /// |
362 | /// For Unix-specific options, import the [`DirEntryExt`][`std::os::unix::fs::DirEntryExt`] trait. |
363 | pub struct DirEntry(Arc<std::fs::DirEntry>); |
364 | |
365 | impl DirEntry { |
366 | /// Returns the full path to this entry. |
367 | /// |
368 | /// The full path is created by joining the original path passed to [`read_dir()`] with the |
369 | /// name of this entry. |
370 | /// |
371 | /// # Examples |
372 | /// |
373 | /// ```no_run |
374 | /// use futures_lite::stream::StreamExt; |
375 | /// |
376 | /// # futures_lite::future::block_on(async { |
377 | /// let mut dir = async_fs::read_dir("." ).await?; |
378 | /// |
379 | /// while let Some(entry) = dir.try_next().await? { |
380 | /// println!("{:?}" , entry.path()); |
381 | /// } |
382 | /// # std::io::Result::Ok(()) }); |
383 | /// ``` |
384 | pub fn path(&self) -> PathBuf { |
385 | self.0.path() |
386 | } |
387 | |
388 | /// Reads the metadata for this entry. |
389 | /// |
390 | /// This function will traverse symbolic links to read the metadata. |
391 | /// |
392 | /// If you want to read metadata without following symbolic links, use [`symlink_metadata()`] |
393 | /// instead. |
394 | /// |
395 | /// # Errors |
396 | /// |
397 | /// An error will be returned in the following situations: |
398 | /// |
399 | /// * This entry does not point to an existing file or directory anymore. |
400 | /// * The current process lacks permissions to read the metadata. |
401 | /// * Some other I/O error occurred. |
402 | /// |
403 | /// # Examples |
404 | /// |
405 | /// ```no_run |
406 | /// use futures_lite::stream::StreamExt; |
407 | /// |
408 | /// # futures_lite::future::block_on(async { |
409 | /// let mut dir = async_fs::read_dir("." ).await?; |
410 | /// |
411 | /// while let Some(entry) = dir.try_next().await? { |
412 | /// println!("{:?}" , entry.metadata().await?); |
413 | /// } |
414 | /// # std::io::Result::Ok(()) }); |
415 | /// ``` |
416 | pub async fn metadata(&self) -> io::Result<Metadata> { |
417 | let inner = self.0.clone(); |
418 | unblock(move || inner.metadata()).await |
419 | } |
420 | |
421 | /// Reads the file type for this entry. |
422 | /// |
423 | /// This function will not traverse symbolic links if this entry points at one. |
424 | /// |
425 | /// If you want to read metadata with following symbolic links, use [`metadata()`] instead. |
426 | /// |
427 | /// # Errors |
428 | /// |
429 | /// An error will be returned in the following situations: |
430 | /// |
431 | /// * This entry does not point to an existing file or directory anymore. |
432 | /// * The current process lacks permissions to read this entry's metadata. |
433 | /// * Some other I/O error occurred. |
434 | /// |
435 | /// # Examples |
436 | /// |
437 | /// ```no_run |
438 | /// use futures_lite::stream::StreamExt; |
439 | /// |
440 | /// # futures_lite::future::block_on(async { |
441 | /// let mut dir = async_fs::read_dir("." ).await?; |
442 | /// |
443 | /// while let Some(entry) = dir.try_next().await? { |
444 | /// println!("{:?}" , entry.file_type().await?); |
445 | /// } |
446 | /// # std::io::Result::Ok(()) }); |
447 | /// ``` |
448 | pub async fn file_type(&self) -> io::Result<FileType> { |
449 | let inner = self.0.clone(); |
450 | unblock(move || inner.file_type()).await |
451 | } |
452 | |
453 | /// Returns the bare name of this entry without the leading path. |
454 | /// |
455 | /// # Examples |
456 | /// |
457 | /// ```no_run |
458 | /// use futures_lite::stream::StreamExt; |
459 | /// |
460 | /// # futures_lite::future::block_on(async { |
461 | /// let mut dir = async_fs::read_dir("." ).await?; |
462 | /// |
463 | /// while let Some(entry) = dir.try_next().await? { |
464 | /// println!("{}" , entry.file_name().to_string_lossy()); |
465 | /// } |
466 | /// # std::io::Result::Ok(()) }); |
467 | /// ``` |
468 | pub fn file_name(&self) -> OsString { |
469 | self.0.file_name() |
470 | } |
471 | } |
472 | |
473 | impl fmt::Debug for DirEntry { |
474 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
475 | f.debug_tuple(name:"DirEntry" ).field(&self.path()).finish() |
476 | } |
477 | } |
478 | |
479 | impl Clone for DirEntry { |
480 | fn clone(&self) -> Self { |
481 | DirEntry(self.0.clone()) |
482 | } |
483 | } |
484 | |
485 | #[cfg (unix)] |
486 | impl unix::DirEntryExt for DirEntry { |
487 | fn ino(&self) -> u64 { |
488 | self.0.ino() |
489 | } |
490 | } |
491 | |
492 | /// Reads a symbolic link and returns the path it points to. |
493 | /// |
494 | /// # Errors |
495 | /// |
496 | /// An error will be returned in the following situations: |
497 | /// |
498 | /// * `path` does not point to an existing link. |
499 | /// * Some other I/O error occurred. |
500 | /// |
501 | /// # Examples |
502 | /// |
503 | /// ```no_run |
504 | /// # futures_lite::future::block_on(async { |
505 | /// let path = async_fs::read_link("a.txt" ).await?; |
506 | /// # std::io::Result::Ok(()) }); |
507 | /// ``` |
508 | pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { |
509 | let path: PathBuf = path.as_ref().to_owned(); |
510 | unblock(move || std::fs::read_link(path)).await |
511 | } |
512 | |
513 | /// Reads the entire contents of a file as a string. |
514 | /// |
515 | /// This is a convenience function for reading entire files. It pre-allocates a string based on the |
516 | /// file size when available, so it is typically faster than manually opening a file and reading |
517 | /// from it. |
518 | /// |
519 | /// If you want to read the contents as raw bytes, use [`read()`] instead. |
520 | /// |
521 | /// # Errors |
522 | /// |
523 | /// An error will be returned in the following situations: |
524 | /// |
525 | /// * `path` does not point to an existing file. |
526 | /// * The current process lacks permissions to read the file. |
527 | /// * The contents of the file cannot be read as a UTF-8 string. |
528 | /// * Some other I/O error occurred. |
529 | /// |
530 | /// # Examples |
531 | /// |
532 | /// ```no_run |
533 | /// # futures_lite::future::block_on(async { |
534 | /// let contents = async_fs::read_to_string("a.txt" ).await?; |
535 | /// # std::io::Result::Ok(()) }); |
536 | /// ``` |
537 | pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> { |
538 | let path: PathBuf = path.as_ref().to_owned(); |
539 | unblock(move || std::fs::read_to_string(path)).await |
540 | } |
541 | |
542 | /// Removes an empty directory. |
543 | /// |
544 | /// Note that this function can only delete an empty directory. If you want to delete a directory |
545 | /// and all of its contents, use [`remove_dir_all()`] instead. |
546 | /// |
547 | /// # Errors |
548 | /// |
549 | /// An error will be returned in the following situations: |
550 | /// |
551 | /// * `path` is not an existing and empty directory. |
552 | /// * The current process lacks permissions to remove the directory. |
553 | /// * Some other I/O error occurred. |
554 | /// |
555 | /// # Examples |
556 | /// |
557 | /// ```no_run |
558 | /// # futures_lite::future::block_on(async { |
559 | /// async_fs::remove_dir("./some/directory" ).await?; |
560 | /// # std::io::Result::Ok(()) }); |
561 | /// ``` |
562 | pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { |
563 | let path: PathBuf = path.as_ref().to_owned(); |
564 | unblock(move || std::fs::remove_dir(path)).await |
565 | } |
566 | |
567 | /// Removes a directory and all of its contents. |
568 | /// |
569 | /// # Errors |
570 | /// |
571 | /// An error will be returned in the following situations: |
572 | /// |
573 | /// * `path` is not an existing directory. |
574 | /// * The current process lacks permissions to remove the directory. |
575 | /// * Some other I/O error occurred. |
576 | /// |
577 | /// # Examples |
578 | /// |
579 | /// ```no_run |
580 | /// # futures_lite::future::block_on(async { |
581 | /// async_fs::remove_dir_all("./some/directory" ).await?; |
582 | /// # std::io::Result::Ok(()) }); |
583 | /// ``` |
584 | pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { |
585 | let path: PathBuf = path.as_ref().to_owned(); |
586 | unblock(move || std::fs::remove_dir_all(path)).await |
587 | } |
588 | |
589 | /// Removes a file. |
590 | /// |
591 | /// # Errors |
592 | /// |
593 | /// An error will be returned in the following situations: |
594 | /// |
595 | /// * `path` does not point to an existing file. |
596 | /// * The current process lacks permissions to remove the file. |
597 | /// * Some other I/O error occurred. |
598 | /// |
599 | /// # Examples |
600 | /// |
601 | /// ```no_run |
602 | /// # futures_lite::future::block_on(async { |
603 | /// async_fs::remove_file("a.txt" ).await?; |
604 | /// # std::io::Result::Ok(()) }); |
605 | /// ``` |
606 | pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> { |
607 | let path: PathBuf = path.as_ref().to_owned(); |
608 | unblock(move || std::fs::remove_file(path)).await |
609 | } |
610 | |
611 | /// Renames a file or directory to a new location. |
612 | /// |
613 | /// If a file or directory already exists at the target location, it will be overwritten by this |
614 | /// operation. |
615 | /// |
616 | /// # Errors |
617 | /// |
618 | /// An error will be returned in the following situations: |
619 | /// |
620 | /// * `src` does not point to an existing file or directory. |
621 | /// * `src` and `dst` are on different filesystems. |
622 | /// * The current process lacks permissions to do the rename operation. |
623 | /// * Some other I/O error occurred. |
624 | /// |
625 | /// # Examples |
626 | /// |
627 | /// ```no_run |
628 | /// # futures_lite::future::block_on(async { |
629 | /// async_fs::rename("a.txt" , "b.txt" ).await?; |
630 | /// # std::io::Result::Ok(()) }); |
631 | /// ``` |
632 | pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { |
633 | let src: PathBuf = src.as_ref().to_owned(); |
634 | let dst: PathBuf = dst.as_ref().to_owned(); |
635 | unblock(move || std::fs::rename(&src, &dst)).await |
636 | } |
637 | |
638 | /// Changes the permissions of a file or directory. |
639 | /// |
640 | /// # Errors |
641 | /// |
642 | /// An error will be returned in the following situations: |
643 | /// |
644 | /// * `path` does not point to an existing file or directory. |
645 | /// * The current process lacks permissions to change attributes on the file or directory. |
646 | /// * Some other I/O error occurred. |
647 | /// |
648 | /// # Examples |
649 | /// |
650 | /// ```no_run |
651 | /// # futures_lite::future::block_on(async { |
652 | /// let mut perm = async_fs::metadata("a.txt" ).await?.permissions(); |
653 | /// perm.set_readonly(true); |
654 | /// async_fs::set_permissions("a.txt" , perm).await?; |
655 | /// # std::io::Result::Ok(()) }); |
656 | /// ``` |
657 | pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> { |
658 | let path: PathBuf = path.as_ref().to_owned(); |
659 | unblock(move || std::fs::set_permissions(path, perm)).await |
660 | } |
661 | |
662 | /// Reads metadata for a path without following symbolic links. |
663 | /// |
664 | /// If you want to follow symbolic links before reading metadata of the target file or directory, |
665 | /// use [`metadata()`] instead. |
666 | /// |
667 | /// # Errors |
668 | /// |
669 | /// An error will be returned in the following situations: |
670 | /// |
671 | /// * `path` does not point to an existing file or directory. |
672 | /// * The current process lacks permissions to read metadata for the path. |
673 | /// * Some other I/O error occurred. |
674 | /// |
675 | /// # Examples |
676 | /// |
677 | /// ```no_run |
678 | /// # futures_lite::future::block_on(async { |
679 | /// let perm = async_fs::symlink_metadata("a.txt" ).await?.permissions(); |
680 | /// # std::io::Result::Ok(()) }); |
681 | /// ``` |
682 | pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { |
683 | let path: PathBuf = path.as_ref().to_owned(); |
684 | unblock(move || std::fs::symlink_metadata(path)).await |
685 | } |
686 | |
687 | /// Writes a slice of bytes as the new contents of a file. |
688 | /// |
689 | /// This function will create a file if it does not exist, and will entirely replace its contents |
690 | /// if it does. |
691 | /// |
692 | /// # Errors |
693 | /// |
694 | /// An error will be returned in the following situations: |
695 | /// |
696 | /// * The file's parent directory does not exist. |
697 | /// * The current process lacks permissions to write to the file. |
698 | /// * Some other I/O error occurred. |
699 | /// |
700 | /// # Examples |
701 | /// |
702 | /// ```no_run |
703 | /// # futures_lite::future::block_on(async { |
704 | /// async_fs::write("a.txt" , b"Hello world!" ).await?; |
705 | /// # std::io::Result::Ok(()) }); |
706 | /// ``` |
707 | pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { |
708 | let path: PathBuf = path.as_ref().to_owned(); |
709 | let contents: Vec = contents.as_ref().to_owned(); |
710 | unblock(move || std::fs::write(&path, contents)).await |
711 | } |
712 | |
713 | /// A builder for creating directories with configurable options. |
714 | /// |
715 | /// For Unix-specific options, import the [`DirBuilderExt`][`std::os::unix::fs::DirBuilderExt`] |
716 | /// trait. |
717 | #[derive (Debug, Default)] |
718 | pub struct DirBuilder { |
719 | /// Set to `true` if non-existent parent directories should be created. |
720 | recursive: bool, |
721 | |
722 | /// Unix mode for newly created directories. |
723 | #[cfg (unix)] |
724 | mode: Option<u32>, |
725 | } |
726 | |
727 | impl DirBuilder { |
728 | /// Creates a blank set of options. |
729 | /// |
730 | /// The [`recursive()`][`DirBuilder::recursive()`] option is initially set to `false`. |
731 | /// |
732 | /// # Examples |
733 | /// |
734 | /// ``` |
735 | /// use async_fs::DirBuilder; |
736 | /// |
737 | /// let builder = DirBuilder::new(); |
738 | /// ``` |
739 | pub fn new() -> DirBuilder { |
740 | #[cfg (not(unix))] |
741 | let builder = DirBuilder { recursive: false }; |
742 | |
743 | #[cfg (unix)] |
744 | let builder = DirBuilder { |
745 | recursive: false, |
746 | mode: None, |
747 | }; |
748 | |
749 | builder |
750 | } |
751 | |
752 | /// Sets the option for recursive mode. |
753 | /// |
754 | /// When set to `true`, this option means all parent directories should be created recursively |
755 | /// if they don't exist. Parents are created with the same permissions as the final directory. |
756 | /// |
757 | /// This option is initially set to `false`. |
758 | /// |
759 | /// # Examples |
760 | /// |
761 | /// ``` |
762 | /// use async_fs::DirBuilder; |
763 | /// |
764 | /// let mut builder = DirBuilder::new(); |
765 | /// builder.recursive(true); |
766 | /// ``` |
767 | pub fn recursive(&mut self, recursive: bool) -> &mut Self { |
768 | self.recursive = recursive; |
769 | self |
770 | } |
771 | |
772 | /// Creates a directory with the configured options. |
773 | /// |
774 | /// It is considered an error if the directory already exists unless recursive mode is enabled. |
775 | /// |
776 | /// # Errors |
777 | /// |
778 | /// An error will be returned in the following situations: |
779 | /// |
780 | /// * `path` already points to an existing file or directory. |
781 | /// * The current process lacks permissions to create the directory or its missing parents. |
782 | /// * Some other I/O error occurred. |
783 | /// |
784 | /// # Examples |
785 | /// |
786 | /// ```no_run |
787 | /// use async_fs::DirBuilder; |
788 | /// |
789 | /// # futures_lite::future::block_on(async { |
790 | /// DirBuilder::new() |
791 | /// .recursive(true) |
792 | /// .create("./some/directory" ) |
793 | /// .await?; |
794 | /// # std::io::Result::Ok(()) }); |
795 | /// ``` |
796 | pub fn create<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<()>> { |
797 | let mut builder = std::fs::DirBuilder::new(); |
798 | builder.recursive(self.recursive); |
799 | |
800 | #[cfg (unix)] |
801 | { |
802 | if let Some(mode) = self.mode { |
803 | std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode); |
804 | } |
805 | } |
806 | |
807 | let path = path.as_ref().to_owned(); |
808 | unblock(move || builder.create(path)) |
809 | } |
810 | } |
811 | |
812 | #[cfg (unix)] |
813 | impl unix::DirBuilderExt for DirBuilder { |
814 | fn mode(&mut self, mode: u32) -> &mut Self { |
815 | self.mode = Some(mode); |
816 | self |
817 | } |
818 | } |
819 | |
820 | /// An open file on the filesystem. |
821 | /// |
822 | /// Depending on what options the file was opened with, this type can be used for reading and/or |
823 | /// writing. |
824 | /// |
825 | /// Files are automatically closed when they get dropped and any errors detected on closing are |
826 | /// ignored. Use the [`sync_all()`][`File::sync_all()`] method before dropping a file if such |
827 | /// errors need to be handled. |
828 | /// |
829 | /// **NOTE:** If writing to a file, make sure to call |
830 | /// [`flush()`][`futures_lite::io::AsyncWriteExt::flush()`], [`sync_data()`][`File::sync_data()`], |
831 | /// or [`sync_all()`][`File::sync_all()`] before dropping the file or else some written data |
832 | /// might get lost! |
833 | /// |
834 | /// # Examples |
835 | /// |
836 | /// Create a new file and write some bytes to it: |
837 | /// |
838 | /// ```no_run |
839 | /// use async_fs::File; |
840 | /// use futures_lite::io::AsyncWriteExt; |
841 | /// |
842 | /// # futures_lite::future::block_on(async { |
843 | /// let mut file = File::create("a.txt" ).await?; |
844 | /// |
845 | /// file.write_all(b"Hello, world!" ).await?; |
846 | /// file.flush().await?; |
847 | /// # std::io::Result::Ok(()) }); |
848 | /// ``` |
849 | /// |
850 | /// Read the contents of a file into a vector of bytes: |
851 | /// |
852 | /// ```no_run |
853 | /// use async_fs::File; |
854 | /// use futures_lite::io::AsyncReadExt; |
855 | /// |
856 | /// # futures_lite::future::block_on(async { |
857 | /// let mut file = File::open("a.txt" ).await?; |
858 | /// |
859 | /// let mut contents = Vec::new(); |
860 | /// file.read_to_end(&mut contents).await?; |
861 | /// # std::io::Result::Ok(()) }); |
862 | /// ``` |
863 | pub struct File { |
864 | /// Always accessible reference to the file. |
865 | file: Arc<std::fs::File>, |
866 | |
867 | /// Performs blocking I/O operations on a thread pool. |
868 | unblock: Mutex<Unblock<ArcFile>>, |
869 | |
870 | /// Logical file cursor, tracked when reading from the file. |
871 | /// |
872 | /// This will be set to an error if the file is not seekable. |
873 | read_pos: Option<io::Result<u64>>, |
874 | |
875 | /// Set to `true` if the file needs flushing. |
876 | is_dirty: bool, |
877 | } |
878 | |
879 | impl File { |
880 | /// Creates an async file from a blocking file. |
881 | fn new(inner: std::fs::File, is_dirty: bool) -> File { |
882 | let file = Arc::new(inner); |
883 | let unblock = Mutex::new(Unblock::new(ArcFile(file.clone()))); |
884 | let read_pos = None; |
885 | File { |
886 | file, |
887 | unblock, |
888 | read_pos, |
889 | is_dirty, |
890 | } |
891 | } |
892 | |
893 | /// Opens a file in read-only mode. |
894 | /// |
895 | /// See the [`OpenOptions::open()`] function for more options. |
896 | /// |
897 | /// # Errors |
898 | /// |
899 | /// An error will be returned in the following situations: |
900 | /// |
901 | /// * `path` does not point to an existing file. |
902 | /// * The current process lacks permissions to read the file. |
903 | /// * Some other I/O error occurred. |
904 | /// |
905 | /// For more details, see the list of errors documented by [`OpenOptions::open()`]. |
906 | /// |
907 | /// # Examples |
908 | /// |
909 | /// ```no_run |
910 | /// use async_fs::File; |
911 | /// |
912 | /// # futures_lite::future::block_on(async { |
913 | /// let file = File::open("a.txt" ).await?; |
914 | /// # std::io::Result::Ok(()) }); |
915 | /// ``` |
916 | pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> { |
917 | let path = path.as_ref().to_owned(); |
918 | let file = unblock(move || std::fs::File::open(path)).await?; |
919 | Ok(File::new(file, false)) |
920 | } |
921 | |
922 | /// Opens a file in write-only mode. |
923 | /// |
924 | /// This method will create a file if it does not exist, and will truncate it if it does. |
925 | /// |
926 | /// See the [`OpenOptions::open`] function for more options. |
927 | /// |
928 | /// # Errors |
929 | /// |
930 | /// An error will be returned in the following situations: |
931 | /// |
932 | /// * The file's parent directory does not exist. |
933 | /// * The current process lacks permissions to write to the file. |
934 | /// * Some other I/O error occurred. |
935 | /// |
936 | /// For more details, see the list of errors documented by [`OpenOptions::open()`]. |
937 | /// |
938 | /// # Examples |
939 | /// |
940 | /// ```no_run |
941 | /// use async_fs::File; |
942 | /// |
943 | /// # futures_lite::future::block_on(async { |
944 | /// let file = File::create("a.txt" ).await?; |
945 | /// # std::io::Result::Ok(()) }); |
946 | /// ``` |
947 | pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> { |
948 | let path = path.as_ref().to_owned(); |
949 | let file = unblock(move || std::fs::File::create(path)).await?; |
950 | Ok(File::new(file, false)) |
951 | } |
952 | |
953 | /// Synchronizes OS-internal buffered contents and metadata to disk. |
954 | /// |
955 | /// This function will ensure that all in-memory data reaches the filesystem. |
956 | /// |
957 | /// This can be used to handle errors that would otherwise only be caught by closing the file. |
958 | /// When a file is dropped, errors in synchronizing this in-memory data are ignored. |
959 | /// |
960 | /// # Examples |
961 | /// |
962 | /// ```no_run |
963 | /// use async_fs::File; |
964 | /// use futures_lite::io::AsyncWriteExt; |
965 | /// |
966 | /// # futures_lite::future::block_on(async { |
967 | /// let mut file = File::create("a.txt" ).await?; |
968 | /// |
969 | /// file.write_all(b"Hello, world!" ).await?; |
970 | /// file.sync_all().await?; |
971 | /// # std::io::Result::Ok(()) }); |
972 | /// ``` |
973 | pub async fn sync_all(&self) -> io::Result<()> { |
974 | let mut inner = self.unblock.lock().await; |
975 | inner.flush().await?; |
976 | let file = self.file.clone(); |
977 | unblock(move || file.sync_all()).await |
978 | } |
979 | |
980 | /// Synchronizes OS-internal buffered contents to disk. |
981 | /// |
982 | /// This is similar to [`sync_all()`][`File::sync_data()`], except that file metadata may not |
983 | /// be synchronized. |
984 | /// |
985 | /// This is intended for use cases that must synchronize the contents of the file, but don't |
986 | /// need the file metadata synchronized to disk. |
987 | /// |
988 | /// Note that some platforms may simply implement this in terms of |
989 | /// [`sync_all()`][`File::sync_data()`]. |
990 | /// |
991 | /// # Examples |
992 | /// |
993 | /// ```no_run |
994 | /// use async_fs::File; |
995 | /// use futures_lite::io::AsyncWriteExt; |
996 | /// |
997 | /// # futures_lite::future::block_on(async { |
998 | /// let mut file = File::create("a.txt" ).await?; |
999 | /// |
1000 | /// file.write_all(b"Hello, world!" ).await?; |
1001 | /// file.sync_data().await?; |
1002 | /// # std::io::Result::Ok(()) }); |
1003 | /// ``` |
1004 | pub async fn sync_data(&self) -> io::Result<()> { |
1005 | let mut inner = self.unblock.lock().await; |
1006 | inner.flush().await?; |
1007 | let file = self.file.clone(); |
1008 | unblock(move || file.sync_data()).await |
1009 | } |
1010 | |
1011 | /// Truncates or extends the file. |
1012 | /// |
1013 | /// If `size` is less than the current file size, then the file will be truncated. If it is |
1014 | /// greater than the current file size, then the file will be extended to `size` and have all |
1015 | /// intermediate data filled with zeros. |
1016 | /// |
1017 | /// The file's cursor stays at the same position, even if the cursor ends up being past the end |
1018 | /// of the file after this operation. |
1019 | /// |
1020 | /// # Examples |
1021 | /// |
1022 | /// ```no_run |
1023 | /// use async_fs::File; |
1024 | /// |
1025 | /// # futures_lite::future::block_on(async { |
1026 | /// let mut file = File::create("a.txt" ).await?; |
1027 | /// file.set_len(10).await?; |
1028 | /// # std::io::Result::Ok(()) }); |
1029 | /// ``` |
1030 | pub async fn set_len(&self, size: u64) -> io::Result<()> { |
1031 | let mut inner = self.unblock.lock().await; |
1032 | inner.flush().await?; |
1033 | let file = self.file.clone(); |
1034 | unblock(move || file.set_len(size)).await |
1035 | } |
1036 | |
1037 | /// Reads the file's metadata. |
1038 | /// |
1039 | /// # Examples |
1040 | /// |
1041 | /// ```no_run |
1042 | /// use async_fs::File; |
1043 | /// |
1044 | /// # futures_lite::future::block_on(async { |
1045 | /// let file = File::open("a.txt" ).await?; |
1046 | /// let metadata = file.metadata().await?; |
1047 | /// # std::io::Result::Ok(()) }); |
1048 | /// ``` |
1049 | pub async fn metadata(&self) -> io::Result<Metadata> { |
1050 | let file = self.file.clone(); |
1051 | unblock(move || file.metadata()).await |
1052 | } |
1053 | |
1054 | /// Changes the permissions on the file. |
1055 | /// |
1056 | /// # Errors |
1057 | /// |
1058 | /// An error will be returned in the following situations: |
1059 | /// |
1060 | /// * The current process lacks permissions to change attributes on the file. |
1061 | /// * Some other I/O error occurred. |
1062 | /// |
1063 | /// # Examples |
1064 | /// |
1065 | /// ```no_run |
1066 | /// use async_fs::File; |
1067 | /// |
1068 | /// # futures_lite::future::block_on(async { |
1069 | /// let file = File::create("a.txt" ).await?; |
1070 | /// |
1071 | /// let mut perms = file.metadata().await?.permissions(); |
1072 | /// perms.set_readonly(true); |
1073 | /// file.set_permissions(perms).await?; |
1074 | /// # std::io::Result::Ok(()) }); |
1075 | /// ``` |
1076 | pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { |
1077 | let file = self.file.clone(); |
1078 | unblock(move || file.set_permissions(perm)).await |
1079 | } |
1080 | |
1081 | /// Repositions the cursor after reading. |
1082 | /// |
1083 | /// When reading from a file, actual file reads run asynchronously in the background, which |
1084 | /// means the real file cursor is usually ahead of the logical cursor, and the data between |
1085 | /// them is buffered in memory. This kind of buffering is an important optimization. |
1086 | /// |
1087 | /// After reading ends, if we decide to perform a write or a seek operation, the real file |
1088 | /// cursor must first be repositioned back to the correct logical position. |
1089 | fn poll_reposition(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> { |
1090 | if let Some(Ok(read_pos)) = self.read_pos { |
1091 | ready!(Pin::new(self.unblock.get_mut()).poll_seek(cx, SeekFrom::Start(read_pos)))?; |
1092 | } |
1093 | self.read_pos = None; |
1094 | Poll::Ready(Ok(())) |
1095 | } |
1096 | } |
1097 | |
1098 | impl fmt::Debug for File { |
1099 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1100 | self.file.fmt(f) |
1101 | } |
1102 | } |
1103 | |
1104 | impl From<std::fs::File> for File { |
1105 | fn from(inner: std::fs::File) -> File { |
1106 | File::new(inner, is_dirty:true) |
1107 | } |
1108 | } |
1109 | |
1110 | #[cfg (unix)] |
1111 | impl std::os::unix::io::AsRawFd for File { |
1112 | fn as_raw_fd(&self) -> std::os::unix::io::RawFd { |
1113 | self.file.as_raw_fd() |
1114 | } |
1115 | } |
1116 | |
1117 | #[cfg (windows)] |
1118 | impl std::os::windows::io::AsRawHandle for File { |
1119 | fn as_raw_handle(&self) -> std::os::windows::io::RawHandle { |
1120 | self.file.as_raw_handle() |
1121 | } |
1122 | } |
1123 | |
1124 | #[cfg (unix)] |
1125 | impl From<std::os::unix::io::OwnedFd> for File { |
1126 | fn from(fd: std::os::unix::io::OwnedFd) -> Self { |
1127 | File::from(std::fs::File::from(fd)) |
1128 | } |
1129 | } |
1130 | |
1131 | #[cfg (windows)] |
1132 | impl From<std::os::windows::io::OwnedHandle> for File { |
1133 | fn from(fd: std::os::windows::io::OwnedHandle) -> Self { |
1134 | File::from(std::fs::File::from(fd)) |
1135 | } |
1136 | } |
1137 | |
1138 | #[cfg (unix)] |
1139 | impl std::os::unix::io::AsFd for File { |
1140 | fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> { |
1141 | self.file.as_fd() |
1142 | } |
1143 | } |
1144 | |
1145 | #[cfg (windows)] |
1146 | impl std::os::windows::io::AsHandle for File { |
1147 | fn as_handle(&self) -> std::os::windows::io::BorrowedHandle<'_> { |
1148 | self.file.as_handle() |
1149 | } |
1150 | } |
1151 | |
1152 | impl AsyncRead for File { |
1153 | fn poll_read( |
1154 | mut self: Pin<&mut Self>, |
1155 | cx: &mut Context<'_>, |
1156 | buf: &mut [u8], |
1157 | ) -> Poll<io::Result<usize>> { |
1158 | // Before reading begins, remember the current cursor position. |
1159 | if self.read_pos.is_none() { |
1160 | // Initialize the logical cursor to the current position in the file. |
1161 | self.read_pos = Some(ready!(self.as_mut().poll_seek(cx, SeekFrom::Current(0)))); |
1162 | } |
1163 | |
1164 | let n: usize = ready!(Pin::new(self.unblock.get_mut()).poll_read(cx, buf))?; |
1165 | |
1166 | // Update the logical cursor if the file is seekable. |
1167 | if let Some(Ok(pos: &mut u64)) = self.read_pos.as_mut() { |
1168 | *pos += n as u64; |
1169 | } |
1170 | |
1171 | Poll::Ready(Ok(n)) |
1172 | } |
1173 | } |
1174 | |
1175 | impl AsyncWrite for File { |
1176 | fn poll_write( |
1177 | mut self: Pin<&mut Self>, |
1178 | cx: &mut Context<'_>, |
1179 | buf: &[u8], |
1180 | ) -> Poll<io::Result<usize>> { |
1181 | ready!(self.poll_reposition(cx))?; |
1182 | self.is_dirty = true; |
1183 | Pin::new(self.unblock.get_mut()).poll_write(cx, buf) |
1184 | } |
1185 | |
1186 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { |
1187 | if self.is_dirty { |
1188 | ready!(Pin::new(self.unblock.get_mut()).poll_flush(cx))?; |
1189 | self.is_dirty = false; |
1190 | } |
1191 | Poll::Ready(Ok(())) |
1192 | } |
1193 | |
1194 | fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { |
1195 | Pin::new(self.unblock.get_mut()).poll_close(cx) |
1196 | } |
1197 | } |
1198 | |
1199 | impl AsyncSeek for File { |
1200 | fn poll_seek( |
1201 | mut self: Pin<&mut Self>, |
1202 | cx: &mut Context<'_>, |
1203 | pos: SeekFrom, |
1204 | ) -> Poll<io::Result<u64>> { |
1205 | ready!(self.poll_reposition(cx))?; |
1206 | Pin::new(self.unblock.get_mut()).poll_seek(cx, pos) |
1207 | } |
1208 | } |
1209 | |
1210 | /// A wrapper around `Arc<std::fs::File>` that implements `Read`, `Write`, and `Seek`. |
1211 | struct ArcFile(Arc<std::fs::File>); |
1212 | |
1213 | impl io::Read for ArcFile { |
1214 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
1215 | (&*self.0).read(buf) |
1216 | } |
1217 | } |
1218 | |
1219 | impl io::Write for ArcFile { |
1220 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
1221 | (&*self.0).write(buf) |
1222 | } |
1223 | |
1224 | fn flush(&mut self) -> io::Result<()> { |
1225 | (&*self.0).flush() |
1226 | } |
1227 | } |
1228 | |
1229 | impl io::Seek for ArcFile { |
1230 | fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { |
1231 | (&*self.0).seek(pos) |
1232 | } |
1233 | } |
1234 | |
1235 | /// A builder for opening files with configurable options. |
1236 | /// |
1237 | /// Files can be opened in [`read`][`OpenOptions::read()`] and/or |
1238 | /// [`write`][`OpenOptions::write()`] mode. |
1239 | /// |
1240 | /// The [`append`][`OpenOptions::append()`] option opens files in a special writing mode that |
1241 | /// moves the file cursor to the end of file before every write operation. |
1242 | /// |
1243 | /// It is also possible to [`truncate`][`OpenOptions::truncate()`] the file right after opening, |
1244 | /// to [`create`][`OpenOptions::create()`] a file if it doesn't exist yet, or to always create a |
1245 | /// new file with [`create_new`][`OpenOptions::create_new()`]. |
1246 | /// |
1247 | /// # Examples |
1248 | /// |
1249 | /// Open a file for reading: |
1250 | /// |
1251 | /// ```no_run |
1252 | /// use async_fs::OpenOptions; |
1253 | /// |
1254 | /// # futures_lite::future::block_on(async { |
1255 | /// let file = OpenOptions::new() |
1256 | /// .read(true) |
1257 | /// .open("a.txt" ) |
1258 | /// .await?; |
1259 | /// # std::io::Result::Ok(()) }); |
1260 | /// ``` |
1261 | /// |
1262 | /// Open a file for both reading and writing, and create it if it doesn't exist yet: |
1263 | /// |
1264 | /// ```no_run |
1265 | /// use async_fs::OpenOptions; |
1266 | /// |
1267 | /// # futures_lite::future::block_on(async { |
1268 | /// let file = OpenOptions::new() |
1269 | /// .read(true) |
1270 | /// .write(true) |
1271 | /// .create(true) |
1272 | /// .open("a.txt" ) |
1273 | /// .await?; |
1274 | /// # std::io::Result::Ok(()) }); |
1275 | /// ``` |
1276 | #[derive (Clone, Debug)] |
1277 | pub struct OpenOptions(std::fs::OpenOptions); |
1278 | |
1279 | impl OpenOptions { |
1280 | /// Creates a blank set of options. |
1281 | /// |
1282 | /// All options are initially set to `false`. |
1283 | /// |
1284 | /// # Examples |
1285 | /// |
1286 | /// ```no_run |
1287 | /// use async_fs::OpenOptions; |
1288 | /// |
1289 | /// # futures_lite::future::block_on(async { |
1290 | /// let file = OpenOptions::new() |
1291 | /// .read(true) |
1292 | /// .open("a.txt" ) |
1293 | /// .await?; |
1294 | /// # std::io::Result::Ok(()) }); |
1295 | /// ``` |
1296 | pub fn new() -> OpenOptions { |
1297 | OpenOptions(std::fs::OpenOptions::new()) |
1298 | } |
1299 | |
1300 | /// Configures the option for read mode. |
1301 | /// |
1302 | /// When set to `true`, this option means the file will be readable after opening. |
1303 | /// |
1304 | /// # Examples |
1305 | /// |
1306 | /// ```no_run |
1307 | /// use async_fs::OpenOptions; |
1308 | /// |
1309 | /// # futures_lite::future::block_on(async { |
1310 | /// let file = OpenOptions::new() |
1311 | /// .read(true) |
1312 | /// .open("a.txt" ) |
1313 | /// .await?; |
1314 | /// # std::io::Result::Ok(()) }); |
1315 | /// ``` |
1316 | pub fn read(&mut self, read: bool) -> &mut OpenOptions { |
1317 | self.0.read(read); |
1318 | self |
1319 | } |
1320 | |
1321 | /// Configures the option for write mode. |
1322 | /// |
1323 | /// When set to `true`, this option means the file will be writable after opening. |
1324 | /// |
1325 | /// If the file already exists, write calls on it will overwrite the previous contents without |
1326 | /// truncating it. |
1327 | /// |
1328 | /// # Examples |
1329 | /// |
1330 | /// ```no_run |
1331 | /// use async_fs::OpenOptions; |
1332 | /// |
1333 | /// # futures_lite::future::block_on(async { |
1334 | /// let file = OpenOptions::new() |
1335 | /// .write(true) |
1336 | /// .open("a.txt" ) |
1337 | /// .await?; |
1338 | /// # std::io::Result::Ok(()) }); |
1339 | /// ``` |
1340 | pub fn write(&mut self, write: bool) -> &mut OpenOptions { |
1341 | self.0.write(write); |
1342 | self |
1343 | } |
1344 | |
1345 | /// Configures the option for append mode. |
1346 | /// |
1347 | /// When set to `true`, this option means the file will be writable after opening and the file |
1348 | /// cursor will be moved to the end of file before every write operaiton. |
1349 | /// |
1350 | /// # Examples |
1351 | /// |
1352 | /// ```no_run |
1353 | /// use async_fs::OpenOptions; |
1354 | /// |
1355 | /// # futures_lite::future::block_on(async { |
1356 | /// let file = OpenOptions::new() |
1357 | /// .append(true) |
1358 | /// .open("a.txt" ) |
1359 | /// .await?; |
1360 | /// # std::io::Result::Ok(()) }); |
1361 | /// ``` |
1362 | pub fn append(&mut self, append: bool) -> &mut OpenOptions { |
1363 | self.0.append(append); |
1364 | self |
1365 | } |
1366 | |
1367 | /// Configures the option for truncating the previous file. |
1368 | /// |
1369 | /// When set to `true`, the file will be truncated to the length of 0 bytes. |
1370 | /// |
1371 | /// The file must be opened in [`write`][`OpenOptions::write()`] or |
1372 | /// [`append`][`OpenOptions::append()`] mode for truncation to work. |
1373 | /// |
1374 | /// # Examples |
1375 | /// |
1376 | /// ```no_run |
1377 | /// use async_fs::OpenOptions; |
1378 | /// |
1379 | /// # futures_lite::future::block_on(async { |
1380 | /// let file = OpenOptions::new() |
1381 | /// .write(true) |
1382 | /// .truncate(true) |
1383 | /// .open("a.txt" ) |
1384 | /// .await?; |
1385 | /// # std::io::Result::Ok(()) }); |
1386 | /// ``` |
1387 | pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { |
1388 | self.0.truncate(truncate); |
1389 | self |
1390 | } |
1391 | |
1392 | /// Configures the option for creating a new file if it doesn't exist. |
1393 | /// |
1394 | /// When set to `true`, this option means a new file will be created if it doesn't exist. |
1395 | /// |
1396 | /// The file must be opened in [`write`][`OpenOptions::write()`] or |
1397 | /// [`append`][`OpenOptions::append()`] mode for file creation to work. |
1398 | /// |
1399 | /// # Examples |
1400 | /// |
1401 | /// ```no_run |
1402 | /// use async_fs::OpenOptions; |
1403 | /// |
1404 | /// # futures_lite::future::block_on(async { |
1405 | /// let file = OpenOptions::new() |
1406 | /// .write(true) |
1407 | /// .create(true) |
1408 | /// .open("a.txt" ) |
1409 | /// .await?; |
1410 | /// # std::io::Result::Ok(()) }); |
1411 | /// ``` |
1412 | pub fn create(&mut self, create: bool) -> &mut OpenOptions { |
1413 | self.0.create(create); |
1414 | self |
1415 | } |
1416 | |
1417 | /// Configures the option for creating a new file or failing if it already exists. |
1418 | /// |
1419 | /// When set to `true`, this option means a new file will be created, or the open operation |
1420 | /// will fail if the file already exists. |
1421 | /// |
1422 | /// The file must be opened in [`write`][`OpenOptions::write()`] or |
1423 | /// [`append`][`OpenOptions::append()`] mode for file creation to work. |
1424 | /// |
1425 | /// # Examples |
1426 | /// |
1427 | /// ```no_run |
1428 | /// use async_fs::OpenOptions; |
1429 | /// |
1430 | /// # futures_lite::future::block_on(async { |
1431 | /// let file = OpenOptions::new() |
1432 | /// .write(true) |
1433 | /// .create_new(true) |
1434 | /// .open("a.txt" ) |
1435 | /// .await?; |
1436 | /// # std::io::Result::Ok(()) }); |
1437 | /// ``` |
1438 | pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { |
1439 | self.0.create_new(create_new); |
1440 | self |
1441 | } |
1442 | |
1443 | /// Opens a file with the configured options. |
1444 | /// |
1445 | /// # Errors |
1446 | /// |
1447 | /// An error will be returned in the following situations: |
1448 | /// |
1449 | /// * The file does not exist and neither [`create`] nor [`create_new`] were set. |
1450 | /// * The file's parent directory does not exist. |
1451 | /// * The current process lacks permissions to open the file in the configured mode. |
1452 | /// * The file already exists and [`create_new`] was set. |
1453 | /// * Invalid combination of options was used, like [`truncate`] was set but [`write`] wasn't, |
1454 | /// or none of [`read`], [`write`], and [`append`] modes was set. |
1455 | /// * An OS-level occurred, like too many files are open or the file name is too long. |
1456 | /// * Some other I/O error occurred. |
1457 | /// |
1458 | /// [`read`]: `OpenOptions::read()` |
1459 | /// [`write`]: `OpenOptions::write()` |
1460 | /// [`append`]: `OpenOptions::append()` |
1461 | /// [`truncate`]: `OpenOptions::truncate()` |
1462 | /// [`create`]: `OpenOptions::create()` |
1463 | /// [`create_new`]: `OpenOptions::create_new()` |
1464 | /// |
1465 | /// # Examples |
1466 | /// |
1467 | /// ```no_run |
1468 | /// use async_fs::OpenOptions; |
1469 | /// |
1470 | /// # futures_lite::future::block_on(async { |
1471 | /// let file = OpenOptions::new() |
1472 | /// .read(true) |
1473 | /// .open("a.txt" ) |
1474 | /// .await?; |
1475 | /// # std::io::Result::Ok(()) }); |
1476 | /// ``` |
1477 | pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> { |
1478 | let path = path.as_ref().to_owned(); |
1479 | let options = self.0.clone(); |
1480 | async move { |
1481 | let file = unblock(move || options.open(path)).await?; |
1482 | Ok(File::new(file, false)) |
1483 | } |
1484 | } |
1485 | } |
1486 | |
1487 | impl Default for OpenOptions { |
1488 | fn default() -> Self { |
1489 | Self::new() |
1490 | } |
1491 | } |
1492 | |
1493 | #[cfg (unix)] |
1494 | impl unix::OpenOptionsExt for OpenOptions { |
1495 | fn mode(&mut self, mode: u32) -> &mut Self { |
1496 | self.0.mode(mode); |
1497 | self |
1498 | } |
1499 | |
1500 | fn custom_flags(&mut self, flags: i32) -> &mut Self { |
1501 | self.0.custom_flags(flags); |
1502 | self |
1503 | } |
1504 | } |
1505 | |
1506 | #[cfg (windows)] |
1507 | impl windows::OpenOptionsExt for OpenOptions { |
1508 | fn access_mode(&mut self, access: u32) -> &mut Self { |
1509 | self.0.access_mode(access); |
1510 | self |
1511 | } |
1512 | |
1513 | fn share_mode(&mut self, val: u32) -> &mut Self { |
1514 | self.0.share_mode(val); |
1515 | self |
1516 | } |
1517 | |
1518 | fn custom_flags(&mut self, flags: u32) -> &mut Self { |
1519 | self.0.custom_flags(flags); |
1520 | self |
1521 | } |
1522 | |
1523 | fn attributes(&mut self, val: u32) -> &mut Self { |
1524 | self.0.attributes(val); |
1525 | self |
1526 | } |
1527 | |
1528 | fn security_qos_flags(&mut self, flags: u32) -> &mut Self { |
1529 | self.0.security_qos_flags(flags); |
1530 | self |
1531 | } |
1532 | } |
1533 | |
1534 | mod __private { |
1535 | #[doc (hidden)] |
1536 | pub trait Sealed {} |
1537 | |
1538 | impl Sealed for super::OpenOptions {} |
1539 | impl Sealed for super::File {} |
1540 | impl Sealed for super::DirBuilder {} |
1541 | impl Sealed for super::DirEntry {} |
1542 | } |
1543 | |
1544 | /// Unix-specific extensions. |
1545 | #[cfg (unix)] |
1546 | pub mod unix { |
1547 | use super::__private::Sealed; |
1548 | use super::*; |
1549 | |
1550 | #[doc (no_inline)] |
1551 | pub use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt}; |
1552 | |
1553 | /// Creates a new symbolic link on the filesystem. |
1554 | /// |
1555 | /// The `dst` path will be a symbolic link pointing to the `src` path. |
1556 | /// |
1557 | /// # Examples |
1558 | /// |
1559 | /// ```no_run |
1560 | /// # futures_lite::future::block_on(async { |
1561 | /// async_fs::unix::symlink("a.txt" , "b.txt" ).await?; |
1562 | /// # std::io::Result::Ok(()) }); |
1563 | /// ``` |
1564 | pub async fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { |
1565 | let src = src.as_ref().to_owned(); |
1566 | let dst = dst.as_ref().to_owned(); |
1567 | unblock(move || std::os::unix::fs::symlink(&src, &dst)).await |
1568 | } |
1569 | |
1570 | /// Unix-specific extensions to [`DirBuilder`]. |
1571 | pub trait DirBuilderExt: Sealed { |
1572 | /// Sets the mode to create new directories with. |
1573 | /// |
1574 | /// This option defaults to `0o777`. |
1575 | /// |
1576 | /// # Examples |
1577 | /// |
1578 | /// ```no_run |
1579 | /// use async_fs::{DirBuilder, unix::DirBuilderExt}; |
1580 | /// |
1581 | /// let mut builder = DirBuilder::new(); |
1582 | /// builder.mode(0o755); |
1583 | /// ``` |
1584 | fn mode(&mut self, mode: u32) -> &mut Self; |
1585 | } |
1586 | |
1587 | /// Unix-specific extension methods for [`DirEntry`]. |
1588 | pub trait DirEntryExt: Sealed { |
1589 | /// Returns the underlying `d_ino` field in the contained `dirent` structure. |
1590 | /// |
1591 | /// # Examples |
1592 | /// |
1593 | /// ```no_run |
1594 | /// use async_fs::unix::DirEntryExt; |
1595 | /// use futures_lite::stream::StreamExt; |
1596 | /// |
1597 | /// # futures_lite::future::block_on(async { |
1598 | /// let mut entries = async_fs::read_dir("." ).await?; |
1599 | /// |
1600 | /// while let Some(entry) = entries.try_next().await? { |
1601 | /// println!("{:?}: {}" , entry.file_name(), entry.ino()); |
1602 | /// } |
1603 | /// # std::io::Result::Ok(()) }); |
1604 | /// ``` |
1605 | fn ino(&self) -> u64; |
1606 | } |
1607 | |
1608 | /// Unix-specific extensions to [`OpenOptions`]. |
1609 | pub trait OpenOptionsExt: Sealed { |
1610 | /// Sets the mode bits that a new file will be created with. |
1611 | /// |
1612 | /// If a new file is created as part of an [`OpenOptions::open()`] call then this |
1613 | /// specified `mode` will be used as the permission bits for the new file. |
1614 | /// |
1615 | /// If no `mode` is set, the default of `0o666` will be used. |
1616 | /// The operating system masks out bits with the system's `umask`, to produce |
1617 | /// the final permissions. |
1618 | /// |
1619 | /// # Examples |
1620 | /// |
1621 | /// ```no_run |
1622 | /// use async_fs::{OpenOptions, unix::OpenOptionsExt}; |
1623 | /// |
1624 | /// # futures_lite::future::block_on(async { |
1625 | /// let mut options = OpenOptions::new(); |
1626 | /// // Read/write permissions for owner and read permissions for others. |
1627 | /// options.mode(0o644); |
1628 | /// let file = options.open("foo.txt" ).await?; |
1629 | /// # std::io::Result::Ok(()) }); |
1630 | /// ``` |
1631 | fn mode(&mut self, mode: u32) -> &mut Self; |
1632 | |
1633 | /// Passes custom flags to the `flags` argument of `open`. |
1634 | /// |
1635 | /// The bits that define the access mode are masked out with `O_ACCMODE`, to |
1636 | /// ensure they do not interfere with the access mode set by Rust's options. |
1637 | /// |
1638 | /// Custom flags can only set flags, not remove flags set by Rust's options. |
1639 | /// This options overwrites any previously set custom flags. |
1640 | /// |
1641 | /// # Examples |
1642 | /// |
1643 | /// ```no_run |
1644 | /// use async_fs::{OpenOptions, unix::OpenOptionsExt}; |
1645 | /// |
1646 | /// # futures_lite::future::block_on(async { |
1647 | /// let mut options = OpenOptions::new(); |
1648 | /// options.write(true); |
1649 | /// options.custom_flags(libc::O_NOFOLLOW); |
1650 | /// let file = options.open("foo.txt" ).await?; |
1651 | /// # std::io::Result::Ok(()) }); |
1652 | /// ``` |
1653 | fn custom_flags(&mut self, flags: i32) -> &mut Self; |
1654 | } |
1655 | } |
1656 | |
1657 | /// Windows-specific extensions. |
1658 | #[cfg (windows)] |
1659 | pub mod windows { |
1660 | use super::__private::Sealed; |
1661 | use super::*; |
1662 | |
1663 | #[doc (no_inline)] |
1664 | pub use std::os::windows::fs::MetadataExt; |
1665 | |
1666 | /// Creates a new directory symbolic link on the filesystem. |
1667 | /// |
1668 | /// The `dst` path will be a directory symbolic link pointing to the `src` path. |
1669 | /// |
1670 | /// # Examples |
1671 | /// |
1672 | /// ```no_run |
1673 | /// # futures_lite::future::block_on(async { |
1674 | /// async_fs::windows::symlink_dir("a", "b").await?; |
1675 | /// # std::io::Result::Ok(()) }); |
1676 | /// ``` |
1677 | pub async fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { |
1678 | let src = src.as_ref().to_owned(); |
1679 | let dst = dst.as_ref().to_owned(); |
1680 | unblock(move || std::os::windows::fs::symlink_dir(&src, &dst)).await |
1681 | } |
1682 | |
1683 | /// Creates a new file symbolic link on the filesystem. |
1684 | /// |
1685 | /// The `dst` path will be a file symbolic link pointing to the `src` path. |
1686 | /// |
1687 | /// # Examples |
1688 | /// |
1689 | /// ```no_run |
1690 | /// # futures_lite::future::block_on(async { |
1691 | /// async_fs::windows::symlink_file("a.txt", "b.txt").await?; |
1692 | /// # std::io::Result::Ok(()) }); |
1693 | /// ``` |
1694 | pub async fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { |
1695 | let src = src.as_ref().to_owned(); |
1696 | let dst = dst.as_ref().to_owned(); |
1697 | unblock(move || std::os::windows::fs::symlink_file(&src, &dst)).await |
1698 | } |
1699 | |
1700 | /// Windows-specific extensions to [`OpenOptions`]. |
1701 | pub trait OpenOptionsExt: Sealed { |
1702 | /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] |
1703 | /// with the specified value. |
1704 | /// |
1705 | /// This will override the `read`, `write`, and `append` flags on the |
1706 | /// [`OpenOptions`] structure. This method provides fine-grained control over |
1707 | /// the permissions to read, write and append data, attributes (like hidden |
1708 | /// and system), and extended attributes. |
1709 | /// |
1710 | /// # Examples |
1711 | /// |
1712 | /// ```no_run |
1713 | /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; |
1714 | /// |
1715 | /// # futures_lite::future::block_on(async { |
1716 | /// // Open without read and write permission, for example if you only need |
1717 | /// // to call `stat` on the file |
1718 | /// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?; |
1719 | /// # std::io::Result::Ok(()) }); |
1720 | /// ``` |
1721 | /// |
1722 | /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea |
1723 | fn access_mode(&mut self, access: u32) -> &mut Self; |
1724 | |
1725 | /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with |
1726 | /// the specified value. |
1727 | /// |
1728 | /// By default `share_mode` is set to |
1729 | /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows |
1730 | /// other processes to read, write, and delete/rename the same file |
1731 | /// while it is open. Removing any of the flags will prevent other |
1732 | /// processes from performing the corresponding operation until the file |
1733 | /// handle is closed. |
1734 | /// |
1735 | /// # Examples |
1736 | /// |
1737 | /// ```no_run |
1738 | /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; |
1739 | /// |
1740 | /// # futures_lite::future::block_on(async { |
1741 | /// // Do not allow others to read or modify this file while we have it open |
1742 | /// // for writing. |
1743 | /// let file = OpenOptions::new() |
1744 | /// .write(true) |
1745 | /// .share_mode(0) |
1746 | /// .open("foo.txt") |
1747 | /// .await?; |
1748 | /// # std::io::Result::Ok(()) }); |
1749 | /// ``` |
1750 | /// |
1751 | /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea |
1752 | fn share_mode(&mut self, val: u32) -> &mut Self; |
1753 | |
1754 | /// Sets extra flags for the `dwFileFlags` argument to the call to |
1755 | /// [`CreateFile2`] to the specified value (or combines it with |
1756 | /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes` |
1757 | /// for [`CreateFile`]). |
1758 | /// |
1759 | /// Custom flags can only set flags, not remove flags set by Rust's options. |
1760 | /// This option overwrites any previously set custom flags. |
1761 | /// |
1762 | /// # Examples |
1763 | /// |
1764 | /// ```no_run |
1765 | /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; |
1766 | /// |
1767 | /// # futures_lite::future::block_on(async { |
1768 | /// let file = OpenOptions::new() |
1769 | /// .create(true) |
1770 | /// .write(true) |
1771 | /// .custom_flags(windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE) |
1772 | /// .open("foo.txt") |
1773 | /// .await?; |
1774 | /// # std::io::Result::Ok(()) }); |
1775 | /// ``` |
1776 | /// |
1777 | /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea |
1778 | /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 |
1779 | fn custom_flags(&mut self, flags: u32) -> &mut Self; |
1780 | |
1781 | /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to |
1782 | /// the specified value (or combines it with `custom_flags` and |
1783 | /// `security_qos_flags` to set the `dwFlagsAndAttributes` for |
1784 | /// [`CreateFile`]). |
1785 | /// |
1786 | /// If a _new_ file is created because it does not yet exist and |
1787 | /// `.create(true)` or `.create_new(true)` are specified, the new file is |
1788 | /// given the attributes declared with `.attributes()`. |
1789 | /// |
1790 | /// If an _existing_ file is opened with `.create(true).truncate(true)`, its |
1791 | /// existing attributes are preserved and combined with the ones declared |
1792 | /// with `.attributes()`. |
1793 | /// |
1794 | /// In all other cases the attributes get ignored. |
1795 | /// |
1796 | /// # Examples |
1797 | /// |
1798 | /// ```no_run |
1799 | /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; |
1800 | /// |
1801 | /// # futures_lite::future::block_on(async { |
1802 | /// let file = OpenOptions::new() |
1803 | /// .write(true) |
1804 | /// .create(true) |
1805 | /// .attributes(windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN) |
1806 | /// .open("foo.txt") |
1807 | /// .await?; |
1808 | /// # std::io::Result::Ok(()) }); |
1809 | /// ``` |
1810 | /// |
1811 | /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea |
1812 | /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 |
1813 | fn attributes(&mut self, val: u32) -> &mut Self; |
1814 | |
1815 | /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to |
1816 | /// the specified value (or combines it with `custom_flags` and `attributes` |
1817 | /// to set the `dwFlagsAndAttributes` for [`CreateFile`]). |
1818 | /// |
1819 | /// By default `security_qos_flags` is not set. It should be specified when |
1820 | /// opening a named pipe, to control to which degree a server process can |
1821 | /// act on behalf of a client process (security impersonation level). |
1822 | /// |
1823 | /// When `security_qos_flags` is not set, a malicious program can gain the |
1824 | /// elevated privileges of a privileged Rust process when it allows opening |
1825 | /// user-specified paths, by tricking it into opening a named pipe. So |
1826 | /// arguably `security_qos_flags` should also be set when opening arbitrary |
1827 | /// paths. However the bits can then conflict with other flags, specifically |
1828 | /// `FILE_FLAG_OPEN_NO_RECALL`. |
1829 | /// |
1830 | /// For information about possible values, see [Impersonation Levels] on the |
1831 | /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set |
1832 | /// automatically when using this method. |
1833 | /// |
1834 | /// # Examples |
1835 | /// |
1836 | /// ```no_run |
1837 | /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; |
1838 | /// |
1839 | /// # futures_lite::future::block_on(async { |
1840 | /// let file = OpenOptions::new() |
1841 | /// .write(true) |
1842 | /// .create(true) |
1843 | /// .security_qos_flags(windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION) |
1844 | /// .open(r"\\.\pipe\MyPipe") |
1845 | /// .await?; |
1846 | /// # std::io::Result::Ok(()) }); |
1847 | /// ``` |
1848 | /// |
1849 | /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea |
1850 | /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 |
1851 | /// [Impersonation Levels]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level |
1852 | fn security_qos_flags(&mut self, flags: u32) -> &mut Self; |
1853 | } |
1854 | } |
1855 | |