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