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