async_fs/lib.rs
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
35use std::ffi::OsString;
36use std::fmt;
37use std::future::Future;
38use std::io::{self, SeekFrom};
39use std::path::{Path, PathBuf};
40use std::pin::Pin;
41use std::sync::Arc;
42use std::task::{Context, Poll};
43
44#[cfg(unix)]
45use std::os::unix::fs::{DirEntryExt as _, OpenOptionsExt as _};
46
47#[cfg(windows)]
48use std::os::windows::fs::OpenOptionsExt as _;
49
50use async_lock::Mutex;
51use blocking::{unblock, Unblock};
52use futures_lite::future::FutureExt;
53use futures_lite::io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt};
54use futures_lite::ready;
55use futures_lite::stream::Stream;
56
57#[doc(no_inline)]
58pub 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/// ```
80pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
81 let path = 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/// ```
111pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<u64> {
112 let src = src.as_ref().to_owned();
113 let dst = 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/// ```
152pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
153 let path = 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/// ```
196pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
197 let path = 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/// ```
220pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
221 let src = src.as_ref().to_owned();
222 let dst = 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/// ```
247pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
248 let path = 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/// ```
275pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
276 let path = 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/// ```
306pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
307 let path = path.as_ref().to_owned();
308 unblock(move || std::fs::read_dir(path).map(|inner| 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.
316pub 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.
321enum State {
322 Idle(Option<std::fs::ReadDir>),
323 Busy(blocking::Task<(std::fs::ReadDir, Option<io::Result<std::fs::DirEntry>>)>),
324}
325
326impl fmt::Debug for ReadDir {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 f.debug_struct("ReadDir").finish()
329 }
330}
331
332impl 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.
363pub struct DirEntry(Arc<std::fs::DirEntry>);
364
365impl 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
473impl fmt::Debug for DirEntry {
474 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475 f.debug_tuple("DirEntry").field(&self.path()).finish()
476 }
477}
478
479impl Clone for DirEntry {
480 fn clone(&self) -> Self {
481 DirEntry(self.0.clone())
482 }
483}
484
485#[cfg(unix)]
486impl 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/// ```
508pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
509 let path = 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/// ```
537pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
538 let path = 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/// ```
562pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
563 let path = 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/// ```
584pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
585 let path = 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/// ```
606pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
607 let path = 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/// ```
632pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
633 let src = src.as_ref().to_owned();
634 let dst = 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/// ```
657pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
658 let path = 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/// ```
682pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
683 let path = 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/// ```
707pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
708 let path = path.as_ref().to_owned();
709 let contents = 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)]
718pub 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
727impl 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)]
813impl 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/// ```
863pub 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
879impl 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
1098impl fmt::Debug for File {
1099 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1100 self.file.fmt(f)
1101 }
1102}
1103
1104impl From<std::fs::File> for File {
1105 fn from(inner: std::fs::File) -> File {
1106 File::new(inner, true)
1107 }
1108}
1109
1110#[cfg(unix)]
1111impl 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)]
1118impl 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)]
1125impl 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)]
1132impl 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)]
1139impl 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)]
1146impl 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
1152impl 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 = 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)) = self.read_pos.as_mut() {
1168 *pos += n as u64;
1169 }
1170
1171 Poll::Ready(Ok(n))
1172 }
1173}
1174
1175impl 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
1199impl 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`.
1211struct ArcFile(Arc<std::fs::File>);
1212
1213impl io::Read for ArcFile {
1214 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1215 (&*self.0).read(buf)
1216 }
1217}
1218
1219impl 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
1229impl 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)]
1277pub struct OpenOptions(std::fs::OpenOptions);
1278
1279impl 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
1487impl Default for OpenOptions {
1488 fn default() -> Self {
1489 Self::new()
1490 }
1491}
1492
1493#[cfg(unix)]
1494impl 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)]
1507impl 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
1534mod __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)]
1546pub 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)]
1659pub 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}