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