├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src └── lib.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | commit-message: 8 | prefix: '' 9 | labels: [] 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | push: 9 | branches: 10 | - master 11 | schedule: 12 | - cron: '0 2 * * 0' 13 | 14 | env: 15 | CARGO_INCREMENTAL: 0 16 | CARGO_NET_GIT_FETCH_WITH_CLI: true 17 | CARGO_NET_RETRY: 10 18 | CARGO_TERM_COLOR: always 19 | RUST_BACKTRACE: 1 20 | RUSTFLAGS: -D warnings 21 | RUSTDOCFLAGS: -D warnings 22 | RUSTUP_MAX_RETRIES: 10 23 | 24 | defaults: 25 | run: 26 | shell: bash 27 | 28 | jobs: 29 | fmt: 30 | uses: smol-rs/.github/.github/workflows/fmt.yml@main 31 | security_audit: 32 | uses: smol-rs/.github/.github/workflows/security_audit.yml@main 33 | permissions: 34 | checks: write 35 | contents: read 36 | issues: write 37 | secrets: inherit 38 | 39 | test: 40 | runs-on: ${{ matrix.os }} 41 | strategy: 42 | fail-fast: false 43 | matrix: 44 | os: [ubuntu-latest, windows-latest, macos-latest] 45 | rust: [nightly, beta, stable] 46 | steps: 47 | - uses: actions/checkout@v4 48 | - name: Install Rust 49 | # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. 50 | run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} 51 | - run: cargo build --all --all-features --all-targets 52 | - name: Run cargo check (without dev-dependencies to catch missing feature flags) 53 | if: startsWith(matrix.rust, 'nightly') 54 | run: cargo check -Z features=dev_dep 55 | - run: cargo test 56 | 57 | msrv: 58 | runs-on: ${{ matrix.os }} 59 | strategy: 60 | fail-fast: false 61 | matrix: 62 | os: [ubuntu-latest, windows-latest] 63 | steps: 64 | - uses: actions/checkout@v4 65 | - name: Install cargo-hack 66 | uses: taiki-e/install-action@cargo-hack 67 | - run: cargo hack build --rust-version 68 | 69 | clippy: 70 | runs-on: ubuntu-latest 71 | steps: 72 | - uses: actions/checkout@v4 73 | - name: Install Rust 74 | run: rustup update stable 75 | - run: cargo clippy --all-features --all-targets 76 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | push: 8 | tags: 9 | - v[0-9]+.* 10 | 11 | jobs: 12 | create-release: 13 | if: github.repository_owner == 'smol-rs' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: taiki-e/create-gh-release-action@v1 18 | with: 19 | changelog: CHANGELOG.md 20 | branch: master 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 2.1.2 2 | 3 | - Ensure that the docs for `create_dir_all` are close to the equivalent function 4 | in libstd. (#35) 5 | 6 | # Version 2.1.1 7 | 8 | - Fix a copy/paste error in documentation. (#33) 9 | 10 | # Version 2.1.0 11 | 12 | - Bump `async-lock` and `futures-lite` to their latest versions. (#27, #28) 13 | 14 | # Version 2.0.0 15 | 16 | - **Breaking:** Seal extension traits. (#20) 17 | - **Breaking:** Remove unsafe implementations of the `FromRawFd`/`FromRawHandle` traits. (#26) 18 | - Avoid using a `build.rs` script for feature autodetection. (#17) 19 | - Remove the `autocfg` dependency. (#18) 20 | - Avoid a heap allocation in the `ReadDir` implementation. (#23) 21 | 22 | # Version 1.6.0 23 | 24 | - Implement I/O safety traits on Rust 1.63+ (#13) 25 | 26 | # Version 1.5.0 27 | 28 | - Replace `&mut self` with `&self` on the following methods: 29 | - `File::sync_data()` 30 | - `File::sync_all()` 31 | - `File::set_len()` 32 | 33 | # Version 1.4.0 34 | 35 | - Define new extension traits instead of implementing those from `std`. 36 | 37 | # Version 1.3.0 38 | 39 | - Implement `FromRawFd`/`FromRawHandle` for `File`. 40 | - Implement `OpenOptionsExt` for `OpenOptions` on Windows. 41 | - Re-export some extension traits into OS-specific modules. 42 | 43 | # Version 1.2.1 44 | 45 | - Optimization: Don't flush if the file is already flushed. 46 | 47 | # Version 1.2.0 48 | 49 | - Update `blocking` to v1.0 50 | 51 | # Version 1.1.2 52 | 53 | - Do not reposition the cursor if the file is not seekable. 54 | 55 | # Version 1.1.1 56 | 57 | - Update dependencies. 58 | 59 | # Version 1.1.0 60 | 61 | - Implement `From` for `File`. 62 | 63 | # Version 1.0.1 64 | 65 | - Fix build error on https://docs.rs 66 | 67 | # Version 1.0.0 68 | 69 | - Initial version 70 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async-fs" 3 | # When publishing a new version: 4 | # - Update CHANGELOG.md 5 | # - Create "v2.x.y" git tag 6 | version = "2.1.2" 7 | authors = ["Stjepan Glavina "] 8 | edition = "2021" 9 | rust-version = "1.63" 10 | description = "Async filesystem primitives" 11 | license = "Apache-2.0 OR MIT" 12 | repository = "https://github.com/smol-rs/async-fs" 13 | homepage = "https://github.com/smol-rs/async-fs" 14 | documentation = "https://docs.rs/async-fs" 15 | keywords = ["asynchronous", "file", "filesystem", "io"] 16 | categories = ["asynchronous", "concurrency"] 17 | exclude = ["/.*"] 18 | 19 | [dependencies] 20 | async-lock = "3.0.0" 21 | blocking = "1.3.0" 22 | futures-lite = "2.0.0" 23 | 24 | [target.'cfg(unix)'.dev-dependencies] 25 | libc = "0.2.78" 26 | 27 | [target.'cfg(windows)'.dev-dependencies.windows-sys] 28 | version = "0.59" 29 | features = [ 30 | "Win32_Storage_FileSystem", 31 | ] 32 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async-fs 2 | 3 | [![CI](https://github.com/smol-rs/async-fs/actions/workflows/ci.yml/badge.svg)]( 4 | https://github.com/smol-rs/async-fs/actions/workflows/ci.yml) 5 | [![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( 6 | https://github.com/smol-rs/async-fs) 7 | [![Cargo](https://img.shields.io/crates/v/async-fs.svg)]( 8 | https://crates.io/crates/async-fs) 9 | [![Documentation](https://docs.rs/async-fs/badge.svg)]( 10 | https://docs.rs/async-fs) 11 | 12 | Async filesystem primitives. 13 | 14 | This crate is an async version of `std::fs`. 15 | 16 | ## Implementation 17 | 18 | This crate uses [`blocking`] to offload blocking I/O onto a thread pool. 19 | 20 | [`blocking`]: https://docs.rs/blocking 21 | 22 | ## Examples 23 | 24 | Create a new file and write some bytes to it: 25 | 26 | ```rust 27 | use async_fs::File; 28 | use futures_lite::io::AsyncWriteExt; 29 | 30 | let mut file = File::create("a.txt").await?; 31 | file.write_all(b"Hello, world!").await?; 32 | file.flush().await?; 33 | ``` 34 | 35 | ## License 36 | 37 | Licensed under either of 38 | 39 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 40 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 41 | 42 | at your option. 43 | 44 | #### Contribution 45 | 46 | Unless you explicitly state otherwise, any contribution intentionally submitted 47 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 48 | dual licensed as above, without any additional terms or conditions. 49 | -------------------------------------------------------------------------------- /src/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 | 35 | use std::ffi::OsString; 36 | use std::fmt; 37 | use std::future::Future; 38 | use std::io::{self, SeekFrom}; 39 | use std::path::{Path, PathBuf}; 40 | use std::pin::Pin; 41 | use std::sync::Arc; 42 | use std::task::{Context, Poll}; 43 | 44 | #[cfg(unix)] 45 | use std::os::unix::fs::{DirEntryExt as _, OpenOptionsExt as _}; 46 | 47 | #[cfg(windows)] 48 | use std::os::windows::fs::OpenOptionsExt as _; 49 | 50 | use async_lock::Mutex; 51 | use blocking::{unblock, Unblock}; 52 | use futures_lite::future::FutureExt; 53 | use futures_lite::io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt}; 54 | use futures_lite::ready; 55 | use futures_lite::stream::Stream; 56 | 57 | #[doc(no_inline)] 58 | pub use std::fs::{FileType, Metadata, Permissions}; 59 | 60 | /// Returns the canonical form of a path. 61 | /// 62 | /// The returned path is in absolute form with all intermediate components normalized and symbolic 63 | /// links resolved. 64 | /// 65 | /// # Errors 66 | /// 67 | /// An error will be returned in the following situations: 68 | /// 69 | /// * `path` does not point to an existing file or directory. 70 | /// * A non-final component in `path` is not a directory. 71 | /// * Some other I/O error occurred. 72 | /// 73 | /// # Examples 74 | /// 75 | /// ```no_run 76 | /// # futures_lite::future::block_on(async { 77 | /// let path = async_fs::canonicalize(".").await?; 78 | /// # std::io::Result::Ok(()) }); 79 | /// ``` 80 | pub async fn canonicalize>(path: P) -> io::Result { 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 | /// ``` 111 | pub async fn copy, Q: AsRef>(src: P, dst: Q) -> io::Result { 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 | /// ``` 152 | pub async fn create_dir>(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 | /// ``` 196 | pub async fn create_dir_all>(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 | /// ``` 220 | pub async fn hard_link, Q: AsRef>(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 | /// ``` 247 | pub async fn metadata>(path: P) -> io::Result { 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 | /// ``` 275 | pub async fn read>(path: P) -> io::Result> { 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 | /// ``` 306 | pub async fn read_dir>(path: P) -> io::Result { 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. 316 | pub struct ReadDir(State); 317 | 318 | /// The state of an asynchronous `ReadDir`. 319 | /// 320 | /// The `ReadDir` can be either idle or busy performing an asynchronous operation. 321 | enum State { 322 | Idle(Option), 323 | Busy(blocking::Task<(std::fs::ReadDir, Option>)>), 324 | } 325 | 326 | impl fmt::Debug for ReadDir { 327 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 328 | f.debug_struct("ReadDir").finish() 329 | } 330 | } 331 | 332 | impl Stream for ReadDir { 333 | type Item = io::Result; 334 | 335 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 336 | loop { 337 | match &mut self.0 { 338 | State::Idle(opt) => { 339 | let mut inner = opt.take().unwrap(); 340 | 341 | // Start the operation asynchronously. 342 | self.0 = State::Busy(unblock(move || { 343 | let next = inner.next(); 344 | (inner, next) 345 | })); 346 | } 347 | // Poll the asynchronous operation the file is currently blocked on. 348 | State::Busy(task) => { 349 | let (inner, opt) = ready!(task.poll(cx)); 350 | self.0 = State::Idle(Some(inner)); 351 | return Poll::Ready(opt.map(|res| res.map(|inner| DirEntry(Arc::new(inner))))); 352 | } 353 | } 354 | } 355 | } 356 | } 357 | 358 | /// An entry in a directory. 359 | /// 360 | /// A stream of entries in a directory is returned by [`read_dir()`]. 361 | /// 362 | /// For Unix-specific options, import the [`DirEntryExt`][`std::os::unix::fs::DirEntryExt`] trait. 363 | pub struct DirEntry(Arc); 364 | 365 | impl DirEntry { 366 | /// Returns the full path to this entry. 367 | /// 368 | /// The full path is created by joining the original path passed to [`read_dir()`] with the 369 | /// name of this entry. 370 | /// 371 | /// # Examples 372 | /// 373 | /// ```no_run 374 | /// use futures_lite::stream::StreamExt; 375 | /// 376 | /// # futures_lite::future::block_on(async { 377 | /// let mut dir = async_fs::read_dir(".").await?; 378 | /// 379 | /// while let Some(entry) = dir.try_next().await? { 380 | /// println!("{:?}", entry.path()); 381 | /// } 382 | /// # std::io::Result::Ok(()) }); 383 | /// ``` 384 | pub fn path(&self) -> PathBuf { 385 | self.0.path() 386 | } 387 | 388 | /// Reads the metadata for this entry. 389 | /// 390 | /// This function will traverse symbolic links to read the metadata. 391 | /// 392 | /// If you want to read metadata without following symbolic links, use [`symlink_metadata()`] 393 | /// instead. 394 | /// 395 | /// # Errors 396 | /// 397 | /// An error will be returned in the following situations: 398 | /// 399 | /// * This entry does not point to an existing file or directory anymore. 400 | /// * The current process lacks permissions to read the metadata. 401 | /// * Some other I/O error occurred. 402 | /// 403 | /// # Examples 404 | /// 405 | /// ```no_run 406 | /// use futures_lite::stream::StreamExt; 407 | /// 408 | /// # futures_lite::future::block_on(async { 409 | /// let mut dir = async_fs::read_dir(".").await?; 410 | /// 411 | /// while let Some(entry) = dir.try_next().await? { 412 | /// println!("{:?}", entry.metadata().await?); 413 | /// } 414 | /// # std::io::Result::Ok(()) }); 415 | /// ``` 416 | pub async fn metadata(&self) -> io::Result { 417 | let inner = self.0.clone(); 418 | unblock(move || inner.metadata()).await 419 | } 420 | 421 | /// Reads the file type for this entry. 422 | /// 423 | /// This function will not traverse symbolic links if this entry points at one. 424 | /// 425 | /// If you want to read metadata with following symbolic links, use [`metadata()`] instead. 426 | /// 427 | /// # Errors 428 | /// 429 | /// An error will be returned in the following situations: 430 | /// 431 | /// * This entry does not point to an existing file or directory anymore. 432 | /// * The current process lacks permissions to read this entry's metadata. 433 | /// * Some other I/O error occurred. 434 | /// 435 | /// # Examples 436 | /// 437 | /// ```no_run 438 | /// use futures_lite::stream::StreamExt; 439 | /// 440 | /// # futures_lite::future::block_on(async { 441 | /// let mut dir = async_fs::read_dir(".").await?; 442 | /// 443 | /// while let Some(entry) = dir.try_next().await? { 444 | /// println!("{:?}", entry.file_type().await?); 445 | /// } 446 | /// # std::io::Result::Ok(()) }); 447 | /// ``` 448 | pub async fn file_type(&self) -> io::Result { 449 | let inner = self.0.clone(); 450 | unblock(move || inner.file_type()).await 451 | } 452 | 453 | /// Returns the bare name of this entry without the leading path. 454 | /// 455 | /// # Examples 456 | /// 457 | /// ```no_run 458 | /// use futures_lite::stream::StreamExt; 459 | /// 460 | /// # futures_lite::future::block_on(async { 461 | /// let mut dir = async_fs::read_dir(".").await?; 462 | /// 463 | /// while let Some(entry) = dir.try_next().await? { 464 | /// println!("{}", entry.file_name().to_string_lossy()); 465 | /// } 466 | /// # std::io::Result::Ok(()) }); 467 | /// ``` 468 | pub fn file_name(&self) -> OsString { 469 | self.0.file_name() 470 | } 471 | } 472 | 473 | impl fmt::Debug for DirEntry { 474 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 475 | f.debug_tuple("DirEntry").field(&self.path()).finish() 476 | } 477 | } 478 | 479 | impl Clone for DirEntry { 480 | fn clone(&self) -> Self { 481 | DirEntry(self.0.clone()) 482 | } 483 | } 484 | 485 | #[cfg(unix)] 486 | impl unix::DirEntryExt for DirEntry { 487 | fn ino(&self) -> u64 { 488 | self.0.ino() 489 | } 490 | } 491 | 492 | /// Reads a symbolic link and returns the path it points to. 493 | /// 494 | /// # Errors 495 | /// 496 | /// An error will be returned in the following situations: 497 | /// 498 | /// * `path` does not point to an existing link. 499 | /// * Some other I/O error occurred. 500 | /// 501 | /// # Examples 502 | /// 503 | /// ```no_run 504 | /// # futures_lite::future::block_on(async { 505 | /// let path = async_fs::read_link("a.txt").await?; 506 | /// # std::io::Result::Ok(()) }); 507 | /// ``` 508 | pub async fn read_link>(path: P) -> io::Result { 509 | let path = path.as_ref().to_owned(); 510 | unblock(move || std::fs::read_link(path)).await 511 | } 512 | 513 | /// Reads the entire contents of a file as a string. 514 | /// 515 | /// This is a convenience function for reading entire files. It pre-allocates a string based on the 516 | /// file size when available, so it is typically faster than manually opening a file and reading 517 | /// from it. 518 | /// 519 | /// If you want to read the contents as raw bytes, use [`read()`] instead. 520 | /// 521 | /// # Errors 522 | /// 523 | /// An error will be returned in the following situations: 524 | /// 525 | /// * `path` does not point to an existing file. 526 | /// * The current process lacks permissions to read the file. 527 | /// * The contents of the file cannot be read as a UTF-8 string. 528 | /// * Some other I/O error occurred. 529 | /// 530 | /// # Examples 531 | /// 532 | /// ```no_run 533 | /// # futures_lite::future::block_on(async { 534 | /// let contents = async_fs::read_to_string("a.txt").await?; 535 | /// # std::io::Result::Ok(()) }); 536 | /// ``` 537 | pub async fn read_to_string>(path: P) -> io::Result { 538 | let path = path.as_ref().to_owned(); 539 | unblock(move || std::fs::read_to_string(path)).await 540 | } 541 | 542 | /// Removes an empty directory. 543 | /// 544 | /// Note that this function can only delete an empty directory. If you want to delete a directory 545 | /// and all of its contents, use [`remove_dir_all()`] instead. 546 | /// 547 | /// # Errors 548 | /// 549 | /// An error will be returned in the following situations: 550 | /// 551 | /// * `path` is not an existing and empty directory. 552 | /// * The current process lacks permissions to remove the directory. 553 | /// * Some other I/O error occurred. 554 | /// 555 | /// # Examples 556 | /// 557 | /// ```no_run 558 | /// # futures_lite::future::block_on(async { 559 | /// async_fs::remove_dir("./some/directory").await?; 560 | /// # std::io::Result::Ok(()) }); 561 | /// ``` 562 | pub async fn remove_dir>(path: P) -> io::Result<()> { 563 | let path = path.as_ref().to_owned(); 564 | unblock(move || std::fs::remove_dir(path)).await 565 | } 566 | 567 | /// Removes a directory and all of its contents. 568 | /// 569 | /// # Errors 570 | /// 571 | /// An error will be returned in the following situations: 572 | /// 573 | /// * `path` is not an existing directory. 574 | /// * The current process lacks permissions to remove the directory. 575 | /// * Some other I/O error occurred. 576 | /// 577 | /// # Examples 578 | /// 579 | /// ```no_run 580 | /// # futures_lite::future::block_on(async { 581 | /// async_fs::remove_dir_all("./some/directory").await?; 582 | /// # std::io::Result::Ok(()) }); 583 | /// ``` 584 | pub async fn remove_dir_all>(path: P) -> io::Result<()> { 585 | let path = path.as_ref().to_owned(); 586 | unblock(move || std::fs::remove_dir_all(path)).await 587 | } 588 | 589 | /// Removes a file. 590 | /// 591 | /// # Errors 592 | /// 593 | /// An error will be returned in the following situations: 594 | /// 595 | /// * `path` does not point to an existing file. 596 | /// * The current process lacks permissions to remove the file. 597 | /// * Some other I/O error occurred. 598 | /// 599 | /// # Examples 600 | /// 601 | /// ```no_run 602 | /// # futures_lite::future::block_on(async { 603 | /// async_fs::remove_file("a.txt").await?; 604 | /// # std::io::Result::Ok(()) }); 605 | /// ``` 606 | pub async fn remove_file>(path: P) -> io::Result<()> { 607 | let path = path.as_ref().to_owned(); 608 | unblock(move || std::fs::remove_file(path)).await 609 | } 610 | 611 | /// Renames a file or directory to a new location. 612 | /// 613 | /// If a file or directory already exists at the target location, it will be overwritten by this 614 | /// operation. 615 | /// 616 | /// # Errors 617 | /// 618 | /// An error will be returned in the following situations: 619 | /// 620 | /// * `src` does not point to an existing file or directory. 621 | /// * `src` and `dst` are on different filesystems. 622 | /// * The current process lacks permissions to do the rename operation. 623 | /// * Some other I/O error occurred. 624 | /// 625 | /// # Examples 626 | /// 627 | /// ```no_run 628 | /// # futures_lite::future::block_on(async { 629 | /// async_fs::rename("a.txt", "b.txt").await?; 630 | /// # std::io::Result::Ok(()) }); 631 | /// ``` 632 | pub async fn rename, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { 633 | let src = src.as_ref().to_owned(); 634 | let dst = dst.as_ref().to_owned(); 635 | unblock(move || std::fs::rename(&src, &dst)).await 636 | } 637 | 638 | /// Changes the permissions of a file or directory. 639 | /// 640 | /// # Errors 641 | /// 642 | /// An error will be returned in the following situations: 643 | /// 644 | /// * `path` does not point to an existing file or directory. 645 | /// * The current process lacks permissions to change attributes on the file or directory. 646 | /// * Some other I/O error occurred. 647 | /// 648 | /// # Examples 649 | /// 650 | /// ```no_run 651 | /// # futures_lite::future::block_on(async { 652 | /// let mut perm = async_fs::metadata("a.txt").await?.permissions(); 653 | /// perm.set_readonly(true); 654 | /// async_fs::set_permissions("a.txt", perm).await?; 655 | /// # std::io::Result::Ok(()) }); 656 | /// ``` 657 | pub async fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { 658 | let path = path.as_ref().to_owned(); 659 | unblock(move || std::fs::set_permissions(path, perm)).await 660 | } 661 | 662 | /// Reads metadata for a path without following symbolic links. 663 | /// 664 | /// If you want to follow symbolic links before reading metadata of the target file or directory, 665 | /// use [`metadata()`] instead. 666 | /// 667 | /// # Errors 668 | /// 669 | /// An error will be returned in the following situations: 670 | /// 671 | /// * `path` does not point to an existing file or directory. 672 | /// * The current process lacks permissions to read metadata for the path. 673 | /// * Some other I/O error occurred. 674 | /// 675 | /// # Examples 676 | /// 677 | /// ```no_run 678 | /// # futures_lite::future::block_on(async { 679 | /// let perm = async_fs::symlink_metadata("a.txt").await?.permissions(); 680 | /// # std::io::Result::Ok(()) }); 681 | /// ``` 682 | pub async fn symlink_metadata>(path: P) -> io::Result { 683 | let path = path.as_ref().to_owned(); 684 | unblock(move || std::fs::symlink_metadata(path)).await 685 | } 686 | 687 | /// Writes a slice of bytes as the new contents of a file. 688 | /// 689 | /// This function will create a file if it does not exist, and will entirely replace its contents 690 | /// if it does. 691 | /// 692 | /// # Errors 693 | /// 694 | /// An error will be returned in the following situations: 695 | /// 696 | /// * The file's parent directory does not exist. 697 | /// * The current process lacks permissions to write to the file. 698 | /// * Some other I/O error occurred. 699 | /// 700 | /// # Examples 701 | /// 702 | /// ```no_run 703 | /// # futures_lite::future::block_on(async { 704 | /// async_fs::write("a.txt", b"Hello world!").await?; 705 | /// # std::io::Result::Ok(()) }); 706 | /// ``` 707 | pub async fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { 708 | let path = path.as_ref().to_owned(); 709 | let contents = contents.as_ref().to_owned(); 710 | unblock(move || std::fs::write(&path, contents)).await 711 | } 712 | 713 | /// A builder for creating directories with configurable options. 714 | /// 715 | /// For Unix-specific options, import the [`DirBuilderExt`][`std::os::unix::fs::DirBuilderExt`] 716 | /// trait. 717 | #[derive(Debug, Default)] 718 | pub struct DirBuilder { 719 | /// Set to `true` if non-existent parent directories should be created. 720 | recursive: bool, 721 | 722 | /// Unix mode for newly created directories. 723 | #[cfg(unix)] 724 | mode: Option, 725 | } 726 | 727 | impl DirBuilder { 728 | /// Creates a blank set of options. 729 | /// 730 | /// The [`recursive()`][`DirBuilder::recursive()`] option is initially set to `false`. 731 | /// 732 | /// # Examples 733 | /// 734 | /// ``` 735 | /// use async_fs::DirBuilder; 736 | /// 737 | /// let builder = DirBuilder::new(); 738 | /// ``` 739 | pub fn new() -> DirBuilder { 740 | #[cfg(not(unix))] 741 | let builder = DirBuilder { recursive: false }; 742 | 743 | #[cfg(unix)] 744 | let builder = DirBuilder { 745 | recursive: false, 746 | mode: None, 747 | }; 748 | 749 | builder 750 | } 751 | 752 | /// Sets the option for recursive mode. 753 | /// 754 | /// When set to `true`, this option means all parent directories should be created recursively 755 | /// if they don't exist. Parents are created with the same permissions as the final directory. 756 | /// 757 | /// This option is initially set to `false`. 758 | /// 759 | /// # Examples 760 | /// 761 | /// ``` 762 | /// use async_fs::DirBuilder; 763 | /// 764 | /// let mut builder = DirBuilder::new(); 765 | /// builder.recursive(true); 766 | /// ``` 767 | pub fn recursive(&mut self, recursive: bool) -> &mut Self { 768 | self.recursive = recursive; 769 | self 770 | } 771 | 772 | /// Creates a directory with the configured options. 773 | /// 774 | /// It is considered an error if the directory already exists unless recursive mode is enabled. 775 | /// 776 | /// # Errors 777 | /// 778 | /// An error will be returned in the following situations: 779 | /// 780 | /// * `path` already points to an existing file or directory. 781 | /// * The current process lacks permissions to create the directory or its missing parents. 782 | /// * Some other I/O error occurred. 783 | /// 784 | /// # Examples 785 | /// 786 | /// ```no_run 787 | /// use async_fs::DirBuilder; 788 | /// 789 | /// # futures_lite::future::block_on(async { 790 | /// DirBuilder::new() 791 | /// .recursive(true) 792 | /// .create("./some/directory") 793 | /// .await?; 794 | /// # std::io::Result::Ok(()) }); 795 | /// ``` 796 | pub fn create>(&self, path: P) -> impl Future> { 797 | let mut builder = std::fs::DirBuilder::new(); 798 | builder.recursive(self.recursive); 799 | 800 | #[cfg(unix)] 801 | { 802 | if let Some(mode) = self.mode { 803 | std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode); 804 | } 805 | } 806 | 807 | let path = path.as_ref().to_owned(); 808 | unblock(move || builder.create(path)) 809 | } 810 | } 811 | 812 | #[cfg(unix)] 813 | impl unix::DirBuilderExt for DirBuilder { 814 | fn mode(&mut self, mode: u32) -> &mut Self { 815 | self.mode = Some(mode); 816 | self 817 | } 818 | } 819 | 820 | /// An open file on the filesystem. 821 | /// 822 | /// Depending on what options the file was opened with, this type can be used for reading and/or 823 | /// writing. 824 | /// 825 | /// Files are automatically closed when they get dropped and any errors detected on closing are 826 | /// ignored. Use the [`sync_all()`][`File::sync_all()`] method before dropping a file if such 827 | /// errors need to be handled. 828 | /// 829 | /// **NOTE:** If writing to a file, make sure to call 830 | /// [`flush()`][`futures_lite::io::AsyncWriteExt::flush()`], [`sync_data()`][`File::sync_data()`], 831 | /// or [`sync_all()`][`File::sync_all()`] before dropping the file or else some written data 832 | /// might get lost! 833 | /// 834 | /// # Examples 835 | /// 836 | /// Create a new file and write some bytes to it: 837 | /// 838 | /// ```no_run 839 | /// use async_fs::File; 840 | /// use futures_lite::io::AsyncWriteExt; 841 | /// 842 | /// # futures_lite::future::block_on(async { 843 | /// let mut file = File::create("a.txt").await?; 844 | /// 845 | /// file.write_all(b"Hello, world!").await?; 846 | /// file.flush().await?; 847 | /// # std::io::Result::Ok(()) }); 848 | /// ``` 849 | /// 850 | /// Read the contents of a file into a vector of bytes: 851 | /// 852 | /// ```no_run 853 | /// use async_fs::File; 854 | /// use futures_lite::io::AsyncReadExt; 855 | /// 856 | /// # futures_lite::future::block_on(async { 857 | /// let mut file = File::open("a.txt").await?; 858 | /// 859 | /// let mut contents = Vec::new(); 860 | /// file.read_to_end(&mut contents).await?; 861 | /// # std::io::Result::Ok(()) }); 862 | /// ``` 863 | pub struct File { 864 | /// Always accessible reference to the file. 865 | file: Arc, 866 | 867 | /// Performs blocking I/O operations on a thread pool. 868 | unblock: Mutex>, 869 | 870 | /// Logical file cursor, tracked when reading from the file. 871 | /// 872 | /// This will be set to an error if the file is not seekable. 873 | read_pos: Option>, 874 | 875 | /// Set to `true` if the file needs flushing. 876 | is_dirty: bool, 877 | } 878 | 879 | impl File { 880 | /// Creates an async file from a blocking file. 881 | fn new(inner: std::fs::File, is_dirty: bool) -> File { 882 | let file = Arc::new(inner); 883 | let unblock = Mutex::new(Unblock::new(ArcFile(file.clone()))); 884 | let read_pos = None; 885 | File { 886 | file, 887 | unblock, 888 | read_pos, 889 | is_dirty, 890 | } 891 | } 892 | 893 | /// Opens a file in read-only mode. 894 | /// 895 | /// See the [`OpenOptions::open()`] function for more options. 896 | /// 897 | /// # Errors 898 | /// 899 | /// An error will be returned in the following situations: 900 | /// 901 | /// * `path` does not point to an existing file. 902 | /// * The current process lacks permissions to read the file. 903 | /// * Some other I/O error occurred. 904 | /// 905 | /// For more details, see the list of errors documented by [`OpenOptions::open()`]. 906 | /// 907 | /// # Examples 908 | /// 909 | /// ```no_run 910 | /// use async_fs::File; 911 | /// 912 | /// # futures_lite::future::block_on(async { 913 | /// let file = File::open("a.txt").await?; 914 | /// # std::io::Result::Ok(()) }); 915 | /// ``` 916 | pub async fn open>(path: P) -> io::Result { 917 | let path = path.as_ref().to_owned(); 918 | let file = unblock(move || std::fs::File::open(path)).await?; 919 | Ok(File::new(file, false)) 920 | } 921 | 922 | /// Opens a file in write-only mode. 923 | /// 924 | /// This method will create a file if it does not exist, and will truncate it if it does. 925 | /// 926 | /// See the [`OpenOptions::open`] function for more options. 927 | /// 928 | /// # Errors 929 | /// 930 | /// An error will be returned in the following situations: 931 | /// 932 | /// * The file's parent directory does not exist. 933 | /// * The current process lacks permissions to write to the file. 934 | /// * Some other I/O error occurred. 935 | /// 936 | /// For more details, see the list of errors documented by [`OpenOptions::open()`]. 937 | /// 938 | /// # Examples 939 | /// 940 | /// ```no_run 941 | /// use async_fs::File; 942 | /// 943 | /// # futures_lite::future::block_on(async { 944 | /// let file = File::create("a.txt").await?; 945 | /// # std::io::Result::Ok(()) }); 946 | /// ``` 947 | pub async fn create>(path: P) -> io::Result { 948 | let path = path.as_ref().to_owned(); 949 | let file = unblock(move || std::fs::File::create(path)).await?; 950 | Ok(File::new(file, false)) 951 | } 952 | 953 | /// Synchronizes OS-internal buffered contents and metadata to disk. 954 | /// 955 | /// This function will ensure that all in-memory data reaches the filesystem. 956 | /// 957 | /// This can be used to handle errors that would otherwise only be caught by closing the file. 958 | /// When a file is dropped, errors in synchronizing this in-memory data are ignored. 959 | /// 960 | /// # Examples 961 | /// 962 | /// ```no_run 963 | /// use async_fs::File; 964 | /// use futures_lite::io::AsyncWriteExt; 965 | /// 966 | /// # futures_lite::future::block_on(async { 967 | /// let mut file = File::create("a.txt").await?; 968 | /// 969 | /// file.write_all(b"Hello, world!").await?; 970 | /// file.sync_all().await?; 971 | /// # std::io::Result::Ok(()) }); 972 | /// ``` 973 | pub async fn sync_all(&self) -> io::Result<()> { 974 | let mut inner = self.unblock.lock().await; 975 | inner.flush().await?; 976 | let file = self.file.clone(); 977 | unblock(move || file.sync_all()).await 978 | } 979 | 980 | /// Synchronizes OS-internal buffered contents to disk. 981 | /// 982 | /// This is similar to [`sync_all()`][`File::sync_data()`], except that file metadata may not 983 | /// be synchronized. 984 | /// 985 | /// This is intended for use cases that must synchronize the contents of the file, but don't 986 | /// need the file metadata synchronized to disk. 987 | /// 988 | /// Note that some platforms may simply implement this in terms of 989 | /// [`sync_all()`][`File::sync_data()`]. 990 | /// 991 | /// # Examples 992 | /// 993 | /// ```no_run 994 | /// use async_fs::File; 995 | /// use futures_lite::io::AsyncWriteExt; 996 | /// 997 | /// # futures_lite::future::block_on(async { 998 | /// let mut file = File::create("a.txt").await?; 999 | /// 1000 | /// file.write_all(b"Hello, world!").await?; 1001 | /// file.sync_data().await?; 1002 | /// # std::io::Result::Ok(()) }); 1003 | /// ``` 1004 | pub async fn sync_data(&self) -> io::Result<()> { 1005 | let mut inner = self.unblock.lock().await; 1006 | inner.flush().await?; 1007 | let file = self.file.clone(); 1008 | unblock(move || file.sync_data()).await 1009 | } 1010 | 1011 | /// Truncates or extends the file. 1012 | /// 1013 | /// If `size` is less than the current file size, then the file will be truncated. If it is 1014 | /// greater than the current file size, then the file will be extended to `size` and have all 1015 | /// intermediate data filled with zeros. 1016 | /// 1017 | /// The file's cursor stays at the same position, even if the cursor ends up being past the end 1018 | /// of the file after this operation. 1019 | /// 1020 | /// # Examples 1021 | /// 1022 | /// ```no_run 1023 | /// use async_fs::File; 1024 | /// 1025 | /// # futures_lite::future::block_on(async { 1026 | /// let mut file = File::create("a.txt").await?; 1027 | /// file.set_len(10).await?; 1028 | /// # std::io::Result::Ok(()) }); 1029 | /// ``` 1030 | pub async fn set_len(&self, size: u64) -> io::Result<()> { 1031 | let mut inner = self.unblock.lock().await; 1032 | inner.flush().await?; 1033 | let file = self.file.clone(); 1034 | unblock(move || file.set_len(size)).await 1035 | } 1036 | 1037 | /// Reads the file's metadata. 1038 | /// 1039 | /// # Examples 1040 | /// 1041 | /// ```no_run 1042 | /// use async_fs::File; 1043 | /// 1044 | /// # futures_lite::future::block_on(async { 1045 | /// let file = File::open("a.txt").await?; 1046 | /// let metadata = file.metadata().await?; 1047 | /// # std::io::Result::Ok(()) }); 1048 | /// ``` 1049 | pub async fn metadata(&self) -> io::Result { 1050 | let file = self.file.clone(); 1051 | unblock(move || file.metadata()).await 1052 | } 1053 | 1054 | /// Changes the permissions on the file. 1055 | /// 1056 | /// # Errors 1057 | /// 1058 | /// An error will be returned in the following situations: 1059 | /// 1060 | /// * The current process lacks permissions to change attributes on the file. 1061 | /// * Some other I/O error occurred. 1062 | /// 1063 | /// # Examples 1064 | /// 1065 | /// ```no_run 1066 | /// use async_fs::File; 1067 | /// 1068 | /// # futures_lite::future::block_on(async { 1069 | /// let file = File::create("a.txt").await?; 1070 | /// 1071 | /// let mut perms = file.metadata().await?.permissions(); 1072 | /// perms.set_readonly(true); 1073 | /// file.set_permissions(perms).await?; 1074 | /// # std::io::Result::Ok(()) }); 1075 | /// ``` 1076 | pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { 1077 | let file = self.file.clone(); 1078 | unblock(move || file.set_permissions(perm)).await 1079 | } 1080 | 1081 | /// Repositions the cursor after reading. 1082 | /// 1083 | /// When reading from a file, actual file reads run asynchronously in the background, which 1084 | /// means the real file cursor is usually ahead of the logical cursor, and the data between 1085 | /// them is buffered in memory. This kind of buffering is an important optimization. 1086 | /// 1087 | /// After reading ends, if we decide to perform a write or a seek operation, the real file 1088 | /// cursor must first be repositioned back to the correct logical position. 1089 | fn poll_reposition(&mut self, cx: &mut Context<'_>) -> Poll> { 1090 | if let Some(Ok(read_pos)) = self.read_pos { 1091 | ready!(Pin::new(self.unblock.get_mut()).poll_seek(cx, SeekFrom::Start(read_pos)))?; 1092 | } 1093 | self.read_pos = None; 1094 | Poll::Ready(Ok(())) 1095 | } 1096 | } 1097 | 1098 | impl fmt::Debug for File { 1099 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1100 | self.file.fmt(f) 1101 | } 1102 | } 1103 | 1104 | impl From for File { 1105 | fn from(inner: std::fs::File) -> File { 1106 | File::new(inner, true) 1107 | } 1108 | } 1109 | 1110 | #[cfg(unix)] 1111 | impl std::os::unix::io::AsRawFd for File { 1112 | fn as_raw_fd(&self) -> std::os::unix::io::RawFd { 1113 | self.file.as_raw_fd() 1114 | } 1115 | } 1116 | 1117 | #[cfg(windows)] 1118 | impl std::os::windows::io::AsRawHandle for File { 1119 | fn as_raw_handle(&self) -> std::os::windows::io::RawHandle { 1120 | self.file.as_raw_handle() 1121 | } 1122 | } 1123 | 1124 | #[cfg(unix)] 1125 | impl From for File { 1126 | fn from(fd: std::os::unix::io::OwnedFd) -> Self { 1127 | File::from(std::fs::File::from(fd)) 1128 | } 1129 | } 1130 | 1131 | #[cfg(windows)] 1132 | impl From for File { 1133 | fn from(fd: std::os::windows::io::OwnedHandle) -> Self { 1134 | File::from(std::fs::File::from(fd)) 1135 | } 1136 | } 1137 | 1138 | #[cfg(unix)] 1139 | impl std::os::unix::io::AsFd for File { 1140 | fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> { 1141 | self.file.as_fd() 1142 | } 1143 | } 1144 | 1145 | #[cfg(windows)] 1146 | impl std::os::windows::io::AsHandle for File { 1147 | fn as_handle(&self) -> std::os::windows::io::BorrowedHandle<'_> { 1148 | self.file.as_handle() 1149 | } 1150 | } 1151 | 1152 | impl AsyncRead for File { 1153 | fn poll_read( 1154 | mut self: Pin<&mut Self>, 1155 | cx: &mut Context<'_>, 1156 | buf: &mut [u8], 1157 | ) -> Poll> { 1158 | // Before reading begins, remember the current cursor position. 1159 | if self.read_pos.is_none() { 1160 | // Initialize the logical cursor to the current position in the file. 1161 | self.read_pos = Some(ready!(self.as_mut().poll_seek(cx, SeekFrom::Current(0)))); 1162 | } 1163 | 1164 | let n = ready!(Pin::new(self.unblock.get_mut()).poll_read(cx, buf))?; 1165 | 1166 | // Update the logical cursor if the file is seekable. 1167 | if let Some(Ok(pos)) = self.read_pos.as_mut() { 1168 | *pos += n as u64; 1169 | } 1170 | 1171 | Poll::Ready(Ok(n)) 1172 | } 1173 | } 1174 | 1175 | impl AsyncWrite for File { 1176 | fn poll_write( 1177 | mut self: Pin<&mut Self>, 1178 | cx: &mut Context<'_>, 1179 | buf: &[u8], 1180 | ) -> Poll> { 1181 | ready!(self.poll_reposition(cx))?; 1182 | self.is_dirty = true; 1183 | Pin::new(self.unblock.get_mut()).poll_write(cx, buf) 1184 | } 1185 | 1186 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 1187 | if self.is_dirty { 1188 | ready!(Pin::new(self.unblock.get_mut()).poll_flush(cx))?; 1189 | self.is_dirty = false; 1190 | } 1191 | Poll::Ready(Ok(())) 1192 | } 1193 | 1194 | fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 1195 | Pin::new(self.unblock.get_mut()).poll_close(cx) 1196 | } 1197 | } 1198 | 1199 | impl AsyncSeek for File { 1200 | fn poll_seek( 1201 | mut self: Pin<&mut Self>, 1202 | cx: &mut Context<'_>, 1203 | pos: SeekFrom, 1204 | ) -> Poll> { 1205 | ready!(self.poll_reposition(cx))?; 1206 | Pin::new(self.unblock.get_mut()).poll_seek(cx, pos) 1207 | } 1208 | } 1209 | 1210 | /// A wrapper around `Arc` that implements `Read`, `Write`, and `Seek`. 1211 | struct ArcFile(Arc); 1212 | 1213 | impl io::Read for ArcFile { 1214 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 1215 | (&*self.0).read(buf) 1216 | } 1217 | } 1218 | 1219 | impl io::Write for ArcFile { 1220 | fn write(&mut self, buf: &[u8]) -> io::Result { 1221 | (&*self.0).write(buf) 1222 | } 1223 | 1224 | fn flush(&mut self) -> io::Result<()> { 1225 | (&*self.0).flush() 1226 | } 1227 | } 1228 | 1229 | impl io::Seek for ArcFile { 1230 | fn seek(&mut self, pos: SeekFrom) -> io::Result { 1231 | (&*self.0).seek(pos) 1232 | } 1233 | } 1234 | 1235 | /// A builder for opening files with configurable options. 1236 | /// 1237 | /// Files can be opened in [`read`][`OpenOptions::read()`] and/or 1238 | /// [`write`][`OpenOptions::write()`] mode. 1239 | /// 1240 | /// The [`append`][`OpenOptions::append()`] option opens files in a special writing mode that 1241 | /// moves the file cursor to the end of file before every write operation. 1242 | /// 1243 | /// It is also possible to [`truncate`][`OpenOptions::truncate()`] the file right after opening, 1244 | /// to [`create`][`OpenOptions::create()`] a file if it doesn't exist yet, or to always create a 1245 | /// new file with [`create_new`][`OpenOptions::create_new()`]. 1246 | /// 1247 | /// # Examples 1248 | /// 1249 | /// Open a file for reading: 1250 | /// 1251 | /// ```no_run 1252 | /// use async_fs::OpenOptions; 1253 | /// 1254 | /// # futures_lite::future::block_on(async { 1255 | /// let file = OpenOptions::new() 1256 | /// .read(true) 1257 | /// .open("a.txt") 1258 | /// .await?; 1259 | /// # std::io::Result::Ok(()) }); 1260 | /// ``` 1261 | /// 1262 | /// Open a file for both reading and writing, and create it if it doesn't exist yet: 1263 | /// 1264 | /// ```no_run 1265 | /// use async_fs::OpenOptions; 1266 | /// 1267 | /// # futures_lite::future::block_on(async { 1268 | /// let file = OpenOptions::new() 1269 | /// .read(true) 1270 | /// .write(true) 1271 | /// .create(true) 1272 | /// .open("a.txt") 1273 | /// .await?; 1274 | /// # std::io::Result::Ok(()) }); 1275 | /// ``` 1276 | #[derive(Clone, Debug)] 1277 | pub struct OpenOptions(std::fs::OpenOptions); 1278 | 1279 | impl OpenOptions { 1280 | /// Creates a blank set of options. 1281 | /// 1282 | /// All options are initially set to `false`. 1283 | /// 1284 | /// # Examples 1285 | /// 1286 | /// ```no_run 1287 | /// use async_fs::OpenOptions; 1288 | /// 1289 | /// # futures_lite::future::block_on(async { 1290 | /// let file = OpenOptions::new() 1291 | /// .read(true) 1292 | /// .open("a.txt") 1293 | /// .await?; 1294 | /// # std::io::Result::Ok(()) }); 1295 | /// ``` 1296 | pub fn new() -> OpenOptions { 1297 | OpenOptions(std::fs::OpenOptions::new()) 1298 | } 1299 | 1300 | /// Configures the option for read mode. 1301 | /// 1302 | /// When set to `true`, this option means the file will be readable after opening. 1303 | /// 1304 | /// # Examples 1305 | /// 1306 | /// ```no_run 1307 | /// use async_fs::OpenOptions; 1308 | /// 1309 | /// # futures_lite::future::block_on(async { 1310 | /// let file = OpenOptions::new() 1311 | /// .read(true) 1312 | /// .open("a.txt") 1313 | /// .await?; 1314 | /// # std::io::Result::Ok(()) }); 1315 | /// ``` 1316 | pub fn read(&mut self, read: bool) -> &mut OpenOptions { 1317 | self.0.read(read); 1318 | self 1319 | } 1320 | 1321 | /// Configures the option for write mode. 1322 | /// 1323 | /// When set to `true`, this option means the file will be writable after opening. 1324 | /// 1325 | /// If the file already exists, write calls on it will overwrite the previous contents without 1326 | /// truncating it. 1327 | /// 1328 | /// # Examples 1329 | /// 1330 | /// ```no_run 1331 | /// use async_fs::OpenOptions; 1332 | /// 1333 | /// # futures_lite::future::block_on(async { 1334 | /// let file = OpenOptions::new() 1335 | /// .write(true) 1336 | /// .open("a.txt") 1337 | /// .await?; 1338 | /// # std::io::Result::Ok(()) }); 1339 | /// ``` 1340 | pub fn write(&mut self, write: bool) -> &mut OpenOptions { 1341 | self.0.write(write); 1342 | self 1343 | } 1344 | 1345 | /// Configures the option for append mode. 1346 | /// 1347 | /// When set to `true`, this option means the file will be writable after opening and the file 1348 | /// cursor will be moved to the end of file before every write operation. 1349 | /// 1350 | /// # Examples 1351 | /// 1352 | /// ```no_run 1353 | /// use async_fs::OpenOptions; 1354 | /// 1355 | /// # futures_lite::future::block_on(async { 1356 | /// let file = OpenOptions::new() 1357 | /// .append(true) 1358 | /// .open("a.txt") 1359 | /// .await?; 1360 | /// # std::io::Result::Ok(()) }); 1361 | /// ``` 1362 | pub fn append(&mut self, append: bool) -> &mut OpenOptions { 1363 | self.0.append(append); 1364 | self 1365 | } 1366 | 1367 | /// Configures the option for truncating the previous file. 1368 | /// 1369 | /// When set to `true`, the file will be truncated to the length of 0 bytes. 1370 | /// 1371 | /// The file must be opened in [`write`][`OpenOptions::write()`] or 1372 | /// [`append`][`OpenOptions::append()`] mode for truncation to work. 1373 | /// 1374 | /// # Examples 1375 | /// 1376 | /// ```no_run 1377 | /// use async_fs::OpenOptions; 1378 | /// 1379 | /// # futures_lite::future::block_on(async { 1380 | /// let file = OpenOptions::new() 1381 | /// .write(true) 1382 | /// .truncate(true) 1383 | /// .open("a.txt") 1384 | /// .await?; 1385 | /// # std::io::Result::Ok(()) }); 1386 | /// ``` 1387 | pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { 1388 | self.0.truncate(truncate); 1389 | self 1390 | } 1391 | 1392 | /// Configures the option for creating a new file if it doesn't exist. 1393 | /// 1394 | /// When set to `true`, this option means a new file will be created if it doesn't exist. 1395 | /// 1396 | /// The file must be opened in [`write`][`OpenOptions::write()`] or 1397 | /// [`append`][`OpenOptions::append()`] mode for file creation to work. 1398 | /// 1399 | /// # Examples 1400 | /// 1401 | /// ```no_run 1402 | /// use async_fs::OpenOptions; 1403 | /// 1404 | /// # futures_lite::future::block_on(async { 1405 | /// let file = OpenOptions::new() 1406 | /// .write(true) 1407 | /// .create(true) 1408 | /// .open("a.txt") 1409 | /// .await?; 1410 | /// # std::io::Result::Ok(()) }); 1411 | /// ``` 1412 | pub fn create(&mut self, create: bool) -> &mut OpenOptions { 1413 | self.0.create(create); 1414 | self 1415 | } 1416 | 1417 | /// Configures the option for creating a new file or failing if it already exists. 1418 | /// 1419 | /// When set to `true`, this option means a new file will be created, or the open operation 1420 | /// will fail if the file already exists. 1421 | /// 1422 | /// The file must be opened in [`write`][`OpenOptions::write()`] or 1423 | /// [`append`][`OpenOptions::append()`] mode for file creation to work. 1424 | /// 1425 | /// # Examples 1426 | /// 1427 | /// ```no_run 1428 | /// use async_fs::OpenOptions; 1429 | /// 1430 | /// # futures_lite::future::block_on(async { 1431 | /// let file = OpenOptions::new() 1432 | /// .write(true) 1433 | /// .create_new(true) 1434 | /// .open("a.txt") 1435 | /// .await?; 1436 | /// # std::io::Result::Ok(()) }); 1437 | /// ``` 1438 | pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { 1439 | self.0.create_new(create_new); 1440 | self 1441 | } 1442 | 1443 | /// Opens a file with the configured options. 1444 | /// 1445 | /// # Errors 1446 | /// 1447 | /// An error will be returned in the following situations: 1448 | /// 1449 | /// * The file does not exist and neither [`create`] nor [`create_new`] were set. 1450 | /// * The file's parent directory does not exist. 1451 | /// * The current process lacks permissions to open the file in the configured mode. 1452 | /// * The file already exists and [`create_new`] was set. 1453 | /// * Invalid combination of options was used, like [`truncate`] was set but [`write`] wasn't, 1454 | /// or none of [`read`], [`write`], and [`append`] modes was set. 1455 | /// * An OS-level occurred, like too many files are open or the file name is too long. 1456 | /// * Some other I/O error occurred. 1457 | /// 1458 | /// [`read`]: `OpenOptions::read()` 1459 | /// [`write`]: `OpenOptions::write()` 1460 | /// [`append`]: `OpenOptions::append()` 1461 | /// [`truncate`]: `OpenOptions::truncate()` 1462 | /// [`create`]: `OpenOptions::create()` 1463 | /// [`create_new`]: `OpenOptions::create_new()` 1464 | /// 1465 | /// # Examples 1466 | /// 1467 | /// ```no_run 1468 | /// use async_fs::OpenOptions; 1469 | /// 1470 | /// # futures_lite::future::block_on(async { 1471 | /// let file = OpenOptions::new() 1472 | /// .read(true) 1473 | /// .open("a.txt") 1474 | /// .await?; 1475 | /// # std::io::Result::Ok(()) }); 1476 | /// ``` 1477 | pub fn open>(&self, path: P) -> impl Future> { 1478 | let path = path.as_ref().to_owned(); 1479 | let options = self.0.clone(); 1480 | async move { 1481 | let file = unblock(move || options.open(path)).await?; 1482 | Ok(File::new(file, false)) 1483 | } 1484 | } 1485 | } 1486 | 1487 | impl Default for OpenOptions { 1488 | fn default() -> Self { 1489 | Self::new() 1490 | } 1491 | } 1492 | 1493 | #[cfg(unix)] 1494 | impl unix::OpenOptionsExt for OpenOptions { 1495 | fn mode(&mut self, mode: u32) -> &mut Self { 1496 | self.0.mode(mode); 1497 | self 1498 | } 1499 | 1500 | fn custom_flags(&mut self, flags: i32) -> &mut Self { 1501 | self.0.custom_flags(flags); 1502 | self 1503 | } 1504 | } 1505 | 1506 | #[cfg(windows)] 1507 | impl windows::OpenOptionsExt for OpenOptions { 1508 | fn access_mode(&mut self, access: u32) -> &mut Self { 1509 | self.0.access_mode(access); 1510 | self 1511 | } 1512 | 1513 | fn share_mode(&mut self, val: u32) -> &mut Self { 1514 | self.0.share_mode(val); 1515 | self 1516 | } 1517 | 1518 | fn custom_flags(&mut self, flags: u32) -> &mut Self { 1519 | self.0.custom_flags(flags); 1520 | self 1521 | } 1522 | 1523 | fn attributes(&mut self, val: u32) -> &mut Self { 1524 | self.0.attributes(val); 1525 | self 1526 | } 1527 | 1528 | fn security_qos_flags(&mut self, flags: u32) -> &mut Self { 1529 | self.0.security_qos_flags(flags); 1530 | self 1531 | } 1532 | } 1533 | 1534 | mod __private { 1535 | #[doc(hidden)] 1536 | pub trait Sealed {} 1537 | 1538 | impl Sealed for super::OpenOptions {} 1539 | impl Sealed for super::File {} 1540 | impl Sealed for super::DirBuilder {} 1541 | impl Sealed for super::DirEntry {} 1542 | } 1543 | 1544 | /// Unix-specific extensions. 1545 | #[cfg(unix)] 1546 | pub mod unix { 1547 | use super::__private::Sealed; 1548 | use super::*; 1549 | 1550 | #[doc(no_inline)] 1551 | pub use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt}; 1552 | 1553 | /// Creates a new symbolic link on the filesystem. 1554 | /// 1555 | /// The `dst` path will be a symbolic link pointing to the `src` path. 1556 | /// 1557 | /// # Examples 1558 | /// 1559 | /// ```no_run 1560 | /// # futures_lite::future::block_on(async { 1561 | /// async_fs::unix::symlink("a.txt", "b.txt").await?; 1562 | /// # std::io::Result::Ok(()) }); 1563 | /// ``` 1564 | pub async fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { 1565 | let src = src.as_ref().to_owned(); 1566 | let dst = dst.as_ref().to_owned(); 1567 | unblock(move || std::os::unix::fs::symlink(&src, &dst)).await 1568 | } 1569 | 1570 | /// Unix-specific extensions to [`DirBuilder`]. 1571 | pub trait DirBuilderExt: Sealed { 1572 | /// Sets the mode to create new directories with. 1573 | /// 1574 | /// This option defaults to `0o777`. 1575 | /// 1576 | /// # Examples 1577 | /// 1578 | /// ```no_run 1579 | /// use async_fs::{DirBuilder, unix::DirBuilderExt}; 1580 | /// 1581 | /// let mut builder = DirBuilder::new(); 1582 | /// builder.mode(0o755); 1583 | /// ``` 1584 | fn mode(&mut self, mode: u32) -> &mut Self; 1585 | } 1586 | 1587 | /// Unix-specific extension methods for [`DirEntry`]. 1588 | pub trait DirEntryExt: Sealed { 1589 | /// Returns the underlying `d_ino` field in the contained `dirent` structure. 1590 | /// 1591 | /// # Examples 1592 | /// 1593 | /// ```no_run 1594 | /// use async_fs::unix::DirEntryExt; 1595 | /// use futures_lite::stream::StreamExt; 1596 | /// 1597 | /// # futures_lite::future::block_on(async { 1598 | /// let mut entries = async_fs::read_dir(".").await?; 1599 | /// 1600 | /// while let Some(entry) = entries.try_next().await? { 1601 | /// println!("{:?}: {}", entry.file_name(), entry.ino()); 1602 | /// } 1603 | /// # std::io::Result::Ok(()) }); 1604 | /// ``` 1605 | fn ino(&self) -> u64; 1606 | } 1607 | 1608 | /// Unix-specific extensions to [`OpenOptions`]. 1609 | pub trait OpenOptionsExt: Sealed { 1610 | /// Sets the mode bits that a new file will be created with. 1611 | /// 1612 | /// If a new file is created as part of an [`OpenOptions::open()`] call then this 1613 | /// specified `mode` will be used as the permission bits for the new file. 1614 | /// 1615 | /// If no `mode` is set, the default of `0o666` will be used. 1616 | /// The operating system masks out bits with the system's `umask`, to produce 1617 | /// the final permissions. 1618 | /// 1619 | /// # Examples 1620 | /// 1621 | /// ```no_run 1622 | /// use async_fs::{OpenOptions, unix::OpenOptionsExt}; 1623 | /// 1624 | /// # futures_lite::future::block_on(async { 1625 | /// let mut options = OpenOptions::new(); 1626 | /// // Read/write permissions for owner and read permissions for others. 1627 | /// options.mode(0o644); 1628 | /// let file = options.open("foo.txt").await?; 1629 | /// # std::io::Result::Ok(()) }); 1630 | /// ``` 1631 | fn mode(&mut self, mode: u32) -> &mut Self; 1632 | 1633 | /// Passes custom flags to the `flags` argument of `open`. 1634 | /// 1635 | /// The bits that define the access mode are masked out with `O_ACCMODE`, to 1636 | /// ensure they do not interfere with the access mode set by Rust's options. 1637 | /// 1638 | /// Custom flags can only set flags, not remove flags set by Rust's options. 1639 | /// This options overwrites any previously set custom flags. 1640 | /// 1641 | /// # Examples 1642 | /// 1643 | /// ```no_run 1644 | /// use async_fs::{OpenOptions, unix::OpenOptionsExt}; 1645 | /// 1646 | /// # futures_lite::future::block_on(async { 1647 | /// let mut options = OpenOptions::new(); 1648 | /// options.write(true); 1649 | /// options.custom_flags(libc::O_NOFOLLOW); 1650 | /// let file = options.open("foo.txt").await?; 1651 | /// # std::io::Result::Ok(()) }); 1652 | /// ``` 1653 | fn custom_flags(&mut self, flags: i32) -> &mut Self; 1654 | } 1655 | } 1656 | 1657 | /// Windows-specific extensions. 1658 | #[cfg(windows)] 1659 | pub mod windows { 1660 | use super::__private::Sealed; 1661 | use super::*; 1662 | 1663 | #[doc(no_inline)] 1664 | pub use std::os::windows::fs::MetadataExt; 1665 | 1666 | /// Creates a new directory symbolic link on the filesystem. 1667 | /// 1668 | /// The `dst` path will be a directory symbolic link pointing to the `src` path. 1669 | /// 1670 | /// # Examples 1671 | /// 1672 | /// ```no_run 1673 | /// # futures_lite::future::block_on(async { 1674 | /// async_fs::windows::symlink_dir("a", "b").await?; 1675 | /// # std::io::Result::Ok(()) }); 1676 | /// ``` 1677 | pub async fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { 1678 | let src = src.as_ref().to_owned(); 1679 | let dst = dst.as_ref().to_owned(); 1680 | unblock(move || std::os::windows::fs::symlink_dir(&src, &dst)).await 1681 | } 1682 | 1683 | /// Creates a new file symbolic link on the filesystem. 1684 | /// 1685 | /// The `dst` path will be a file symbolic link pointing to the `src` path. 1686 | /// 1687 | /// # Examples 1688 | /// 1689 | /// ```no_run 1690 | /// # futures_lite::future::block_on(async { 1691 | /// async_fs::windows::symlink_file("a.txt", "b.txt").await?; 1692 | /// # std::io::Result::Ok(()) }); 1693 | /// ``` 1694 | pub async fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { 1695 | let src = src.as_ref().to_owned(); 1696 | let dst = dst.as_ref().to_owned(); 1697 | unblock(move || std::os::windows::fs::symlink_file(&src, &dst)).await 1698 | } 1699 | 1700 | /// Windows-specific extensions to [`OpenOptions`]. 1701 | pub trait OpenOptionsExt: Sealed { 1702 | /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] 1703 | /// with the specified value. 1704 | /// 1705 | /// This will override the `read`, `write`, and `append` flags on the 1706 | /// [`OpenOptions`] structure. This method provides fine-grained control over 1707 | /// the permissions to read, write and append data, attributes (like hidden 1708 | /// and system), and extended attributes. 1709 | /// 1710 | /// # Examples 1711 | /// 1712 | /// ```no_run 1713 | /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; 1714 | /// 1715 | /// # futures_lite::future::block_on(async { 1716 | /// // Open without read and write permission, for example if you only need 1717 | /// // to call `stat` on the file 1718 | /// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?; 1719 | /// # std::io::Result::Ok(()) }); 1720 | /// ``` 1721 | /// 1722 | /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea 1723 | fn access_mode(&mut self, access: u32) -> &mut Self; 1724 | 1725 | /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with 1726 | /// the specified value. 1727 | /// 1728 | /// By default `share_mode` is set to 1729 | /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows 1730 | /// other processes to read, write, and delete/rename the same file 1731 | /// while it is open. Removing any of the flags will prevent other 1732 | /// processes from performing the corresponding operation until the file 1733 | /// handle is closed. 1734 | /// 1735 | /// # Examples 1736 | /// 1737 | /// ```no_run 1738 | /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; 1739 | /// 1740 | /// # futures_lite::future::block_on(async { 1741 | /// // Do not allow others to read or modify this file while we have it open 1742 | /// // for writing. 1743 | /// let file = OpenOptions::new() 1744 | /// .write(true) 1745 | /// .share_mode(0) 1746 | /// .open("foo.txt") 1747 | /// .await?; 1748 | /// # std::io::Result::Ok(()) }); 1749 | /// ``` 1750 | /// 1751 | /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea 1752 | fn share_mode(&mut self, val: u32) -> &mut Self; 1753 | 1754 | /// Sets extra flags for the `dwFileFlags` argument to the call to 1755 | /// [`CreateFile2`] to the specified value (or combines it with 1756 | /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes` 1757 | /// for [`CreateFile`]). 1758 | /// 1759 | /// Custom flags can only set flags, not remove flags set by Rust's options. 1760 | /// This option overwrites any previously set custom flags. 1761 | /// 1762 | /// # Examples 1763 | /// 1764 | /// ```no_run 1765 | /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; 1766 | /// 1767 | /// # futures_lite::future::block_on(async { 1768 | /// let file = OpenOptions::new() 1769 | /// .create(true) 1770 | /// .write(true) 1771 | /// .custom_flags(windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE) 1772 | /// .open("foo.txt") 1773 | /// .await?; 1774 | /// # std::io::Result::Ok(()) }); 1775 | /// ``` 1776 | /// 1777 | /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea 1778 | /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 1779 | fn custom_flags(&mut self, flags: u32) -> &mut Self; 1780 | 1781 | /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to 1782 | /// the specified value (or combines it with `custom_flags` and 1783 | /// `security_qos_flags` to set the `dwFlagsAndAttributes` for 1784 | /// [`CreateFile`]). 1785 | /// 1786 | /// If a _new_ file is created because it does not yet exist and 1787 | /// `.create(true)` or `.create_new(true)` are specified, the new file is 1788 | /// given the attributes declared with `.attributes()`. 1789 | /// 1790 | /// If an _existing_ file is opened with `.create(true).truncate(true)`, its 1791 | /// existing attributes are preserved and combined with the ones declared 1792 | /// with `.attributes()`. 1793 | /// 1794 | /// In all other cases the attributes get ignored. 1795 | /// 1796 | /// # Examples 1797 | /// 1798 | /// ```no_run 1799 | /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; 1800 | /// 1801 | /// # futures_lite::future::block_on(async { 1802 | /// let file = OpenOptions::new() 1803 | /// .write(true) 1804 | /// .create(true) 1805 | /// .attributes(windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN) 1806 | /// .open("foo.txt") 1807 | /// .await?; 1808 | /// # std::io::Result::Ok(()) }); 1809 | /// ``` 1810 | /// 1811 | /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea 1812 | /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 1813 | fn attributes(&mut self, val: u32) -> &mut Self; 1814 | 1815 | /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to 1816 | /// the specified value (or combines it with `custom_flags` and `attributes` 1817 | /// to set the `dwFlagsAndAttributes` for [`CreateFile`]). 1818 | /// 1819 | /// By default `security_qos_flags` is not set. It should be specified when 1820 | /// opening a named pipe, to control to which degree a server process can 1821 | /// act on behalf of a client process (security impersonation level). 1822 | /// 1823 | /// When `security_qos_flags` is not set, a malicious program can gain the 1824 | /// elevated privileges of a privileged Rust process when it allows opening 1825 | /// user-specified paths, by tricking it into opening a named pipe. So 1826 | /// arguably `security_qos_flags` should also be set when opening arbitrary 1827 | /// paths. However the bits can then conflict with other flags, specifically 1828 | /// `FILE_FLAG_OPEN_NO_RECALL`. 1829 | /// 1830 | /// For information about possible values, see [Impersonation Levels] on the 1831 | /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set 1832 | /// automatically when using this method. 1833 | /// 1834 | /// # Examples 1835 | /// 1836 | /// ```no_run 1837 | /// use async_fs::{OpenOptions, windows::OpenOptionsExt}; 1838 | /// 1839 | /// # futures_lite::future::block_on(async { 1840 | /// let file = OpenOptions::new() 1841 | /// .write(true) 1842 | /// .create(true) 1843 | /// .security_qos_flags(windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION) 1844 | /// .open(r"\\.\pipe\MyPipe") 1845 | /// .await?; 1846 | /// # std::io::Result::Ok(()) }); 1847 | /// ``` 1848 | /// 1849 | /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea 1850 | /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 1851 | /// [Impersonation Levels]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level 1852 | fn security_qos_flags(&mut self, flags: u32) -> &mut Self; 1853 | } 1854 | } 1855 | --------------------------------------------------------------------------------