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