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