tokio\fs/open_options.rs
1use crate::fs::{asyncify, File};
2
3use std::io;
4use std::path::Path;
5
6cfg_io_uring! {
7 mod uring_open_options;
8 pub(crate) use uring_open_options::UringOpenOptions;
9 use crate::runtime::driver::op::Op;
10}
11
12#[cfg(test)]
13mod mock_open_options;
14#[cfg(test)]
15use mock_open_options::MockOpenOptions as StdOpenOptions;
16#[cfg(not(test))]
17use std::fs::OpenOptions as StdOpenOptions;
18
19#[cfg(unix)]
20use std::os::unix::fs::OpenOptionsExt;
21#[cfg(windows)]
22use std::os::windows::fs::OpenOptionsExt;
23
24/// Options and flags which can be used to configure how a file is opened.
25///
26/// This builder exposes the ability to configure how a [`File`] is opened and
27/// what operations are permitted on the open file. The [`File::open`] and
28/// [`File::create`] methods are aliases for commonly used options using this
29/// builder.
30///
31/// Generally speaking, when using `OpenOptions`, you'll first call [`new`],
32/// then chain calls to methods to set each option, then call [`open`], passing
33/// the path of the file you're trying to open. This will give you a
34/// [`io::Result`] with a [`File`] inside that you can further operate
35/// on.
36///
37/// This is a specialized version of [`std::fs::OpenOptions`] for usage from
38/// the Tokio runtime.
39///
40/// `From<std::fs::OpenOptions>` is implemented for more advanced configuration
41/// than the methods provided here.
42///
43/// [`new`]: OpenOptions::new
44/// [`open`]: OpenOptions::open
45/// [`File`]: File
46/// [`File::open`]: File::open
47/// [`File::create`]: File::create
48///
49/// # Examples
50///
51/// Opening a file to read:
52///
53/// ```no_run
54/// use tokio::fs::OpenOptions;
55/// use std::io;
56///
57/// #[tokio::main]
58/// async fn main() -> io::Result<()> {
59/// let file = OpenOptions::new()
60/// .read(true)
61/// .open("foo.txt")
62/// .await?;
63///
64/// Ok(())
65/// }
66/// ```
67///
68/// Opening a file for both reading and writing, as well as creating it if it
69/// doesn't exist:
70///
71/// ```no_run
72/// use tokio::fs::OpenOptions;
73/// use std::io;
74///
75/// #[tokio::main]
76/// async fn main() -> io::Result<()> {
77/// let file = OpenOptions::new()
78/// .read(true)
79/// .write(true)
80/// .create(true)
81/// .open("foo.txt")
82/// .await?;
83///
84/// Ok(())
85/// }
86/// ```
87#[derive(Clone, Debug)]
88pub struct OpenOptions {
89 inner: Kind,
90}
91
92#[derive(Debug, Clone)]
93enum Kind {
94 Std(StdOpenOptions),
95 #[cfg(all(
96 tokio_unstable,
97 feature = "io-uring",
98 feature = "rt",
99 feature = "fs",
100 target_os = "linux"
101 ))]
102 Uring(UringOpenOptions),
103}
104
105impl OpenOptions {
106 /// Creates a blank new set of options ready for configuration.
107 ///
108 /// All options are initially set to `false`.
109 ///
110 /// This is an async version of [`std::fs::OpenOptions::new`][std]
111 ///
112 /// [std]: std::fs::OpenOptions::new
113 ///
114 /// # Examples
115 ///
116 /// ```no_run
117 /// use tokio::fs::OpenOptions;
118 ///
119 /// let mut options = OpenOptions::new();
120 /// let future = options.read(true).open("foo.txt");
121 /// ```
122 pub fn new() -> OpenOptions {
123 #[cfg(all(
124 tokio_unstable,
125 feature = "io-uring",
126 feature = "rt",
127 feature = "fs",
128 target_os = "linux"
129 ))]
130 let inner = Kind::Uring(UringOpenOptions::new());
131 #[cfg(not(all(
132 tokio_unstable,
133 feature = "io-uring",
134 feature = "rt",
135 feature = "fs",
136 target_os = "linux"
137 )))]
138 let inner = Kind::Std(StdOpenOptions::new());
139
140 OpenOptions { inner }
141 }
142
143 /// Sets the option for read access.
144 ///
145 /// This option, when true, will indicate that the file should be
146 /// `read`-able if opened.
147 ///
148 /// This is an async version of [`std::fs::OpenOptions::read`][std]
149 ///
150 /// [std]: std::fs::OpenOptions::read
151 ///
152 /// # Examples
153 ///
154 /// ```no_run
155 /// use tokio::fs::OpenOptions;
156 /// use std::io;
157 ///
158 /// #[tokio::main]
159 /// async fn main() -> io::Result<()> {
160 /// let file = OpenOptions::new()
161 /// .read(true)
162 /// .open("foo.txt")
163 /// .await?;
164 ///
165 /// Ok(())
166 /// }
167 /// ```
168 pub fn read(&mut self, read: bool) -> &mut OpenOptions {
169 match &mut self.inner {
170 Kind::Std(opts) => {
171 opts.read(read);
172 }
173 #[cfg(all(
174 tokio_unstable,
175 feature = "io-uring",
176 feature = "rt",
177 feature = "fs",
178 target_os = "linux"
179 ))]
180 Kind::Uring(opts) => {
181 opts.read(read);
182 }
183 }
184 self
185 }
186
187 /// Sets the option for write access.
188 ///
189 /// This option, when true, will indicate that the file should be
190 /// `write`-able if opened.
191 ///
192 /// This is an async version of [`std::fs::OpenOptions::write`][std]
193 ///
194 /// [std]: std::fs::OpenOptions::write
195 ///
196 /// # Examples
197 ///
198 /// ```no_run
199 /// use tokio::fs::OpenOptions;
200 /// use std::io;
201 ///
202 /// #[tokio::main]
203 /// async fn main() -> io::Result<()> {
204 /// let file = OpenOptions::new()
205 /// .write(true)
206 /// .open("foo.txt")
207 /// .await?;
208 ///
209 /// Ok(())
210 /// }
211 /// ```
212 pub fn write(&mut self, write: bool) -> &mut OpenOptions {
213 match &mut self.inner {
214 Kind::Std(opts) => {
215 opts.write(write);
216 }
217 #[cfg(all(
218 tokio_unstable,
219 feature = "io-uring",
220 feature = "rt",
221 feature = "fs",
222 target_os = "linux"
223 ))]
224 Kind::Uring(opts) => {
225 opts.write(write);
226 }
227 }
228 self
229 }
230
231 /// Sets the option for the append mode.
232 ///
233 /// This option, when true, means that writes will append to a file instead
234 /// of overwriting previous contents. Note that setting
235 /// `.write(true).append(true)` has the same effect as setting only
236 /// `.append(true)`.
237 ///
238 /// For most filesystems, the operating system guarantees that all writes are
239 /// atomic: no writes get mangled because another process writes at the same
240 /// time.
241 ///
242 /// One maybe obvious note when using append-mode: make sure that all data
243 /// that belongs together is written to the file in one operation. This
244 /// can be done by concatenating strings before passing them to [`write()`],
245 /// or using a buffered writer (with a buffer of adequate size),
246 /// and calling [`flush()`] when the message is complete.
247 ///
248 /// If a file is opened with both read and append access, beware that after
249 /// opening, and after every write, the position for reading may be set at the
250 /// end of the file. So, before writing, save the current position (using
251 /// [`seek`]`(`[`SeekFrom`]`::`[`Current`]`(0))`), and restore it before the next read.
252 ///
253 /// This is an async version of [`std::fs::OpenOptions::append`][std]
254 ///
255 /// [std]: std::fs::OpenOptions::append
256 ///
257 /// ## Note
258 ///
259 /// This function doesn't create the file if it doesn't exist. Use the [`create`]
260 /// method to do so.
261 ///
262 /// [`write()`]: crate::io::AsyncWriteExt::write
263 /// [`flush()`]: crate::io::AsyncWriteExt::flush
264 /// [`seek`]: crate::io::AsyncSeekExt::seek
265 /// [`SeekFrom`]: std::io::SeekFrom
266 /// [`Current`]: std::io::SeekFrom::Current
267 /// [`create`]: OpenOptions::create
268 ///
269 /// # Examples
270 ///
271 /// ```no_run
272 /// use tokio::fs::OpenOptions;
273 /// use std::io;
274 ///
275 /// #[tokio::main]
276 /// async fn main() -> io::Result<()> {
277 /// let file = OpenOptions::new()
278 /// .append(true)
279 /// .open("foo.txt")
280 /// .await?;
281 ///
282 /// Ok(())
283 /// }
284 /// ```
285 pub fn append(&mut self, append: bool) -> &mut OpenOptions {
286 match &mut self.inner {
287 Kind::Std(opts) => {
288 opts.append(append);
289 }
290 #[cfg(all(
291 tokio_unstable,
292 feature = "io-uring",
293 feature = "rt",
294 feature = "fs",
295 target_os = "linux"
296 ))]
297 Kind::Uring(opts) => {
298 opts.append(append);
299 }
300 }
301 self
302 }
303
304 /// Sets the option for truncating a previous file.
305 ///
306 /// If a file is successfully opened with this option set it will truncate
307 /// the file to 0 length if it already exists.
308 ///
309 /// The file must be opened with write access for truncate to work.
310 ///
311 /// This is an async version of [`std::fs::OpenOptions::truncate`][std]
312 ///
313 /// [std]: std::fs::OpenOptions::truncate
314 ///
315 /// # Examples
316 ///
317 /// ```no_run
318 /// use tokio::fs::OpenOptions;
319 /// use std::io;
320 ///
321 /// #[tokio::main]
322 /// async fn main() -> io::Result<()> {
323 /// let file = OpenOptions::new()
324 /// .write(true)
325 /// .truncate(true)
326 /// .open("foo.txt")
327 /// .await?;
328 ///
329 /// Ok(())
330 /// }
331 /// ```
332 pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
333 match &mut self.inner {
334 Kind::Std(opts) => {
335 opts.truncate(truncate);
336 }
337 #[cfg(all(
338 tokio_unstable,
339 feature = "io-uring",
340 feature = "rt",
341 feature = "fs",
342 target_os = "linux"
343 ))]
344 Kind::Uring(opts) => {
345 opts.truncate(truncate);
346 }
347 }
348 self
349 }
350
351 /// Sets the option for creating a new file.
352 ///
353 /// This option indicates whether a new file will be created if the file
354 /// does not yet already exist.
355 ///
356 /// In order for the file to be created, [`write`] or [`append`] access must
357 /// be used.
358 ///
359 /// This is an async version of [`std::fs::OpenOptions::create`][std]
360 ///
361 /// [std]: std::fs::OpenOptions::create
362 /// [`write`]: OpenOptions::write
363 /// [`append`]: OpenOptions::append
364 ///
365 /// # Examples
366 ///
367 /// ```no_run
368 /// use tokio::fs::OpenOptions;
369 /// use std::io;
370 ///
371 /// #[tokio::main]
372 /// async fn main() -> io::Result<()> {
373 /// let file = OpenOptions::new()
374 /// .write(true)
375 /// .create(true)
376 /// .open("foo.txt")
377 /// .await?;
378 ///
379 /// Ok(())
380 /// }
381 /// ```
382 pub fn create(&mut self, create: bool) -> &mut OpenOptions {
383 match &mut self.inner {
384 Kind::Std(opts) => {
385 opts.create(create);
386 }
387 #[cfg(all(
388 tokio_unstable,
389 feature = "io-uring",
390 feature = "rt",
391 feature = "fs",
392 target_os = "linux"
393 ))]
394 Kind::Uring(opts) => {
395 opts.create(create);
396 }
397 }
398 self
399 }
400
401 /// Sets the option to always create a new file.
402 ///
403 /// This option indicates whether a new file will be created. No file is
404 /// allowed to exist at the target location, also no (dangling) symlink.
405 ///
406 /// This option is useful because it is atomic. Otherwise between checking
407 /// whether a file exists and creating a new one, the file may have been
408 /// created by another process (a TOCTOU race condition / attack).
409 ///
410 /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are
411 /// ignored.
412 ///
413 /// The file must be opened with write or append access in order to create a
414 /// new file.
415 ///
416 /// This is an async version of [`std::fs::OpenOptions::create_new`][std]
417 ///
418 /// [std]: std::fs::OpenOptions::create_new
419 /// [`.create()`]: OpenOptions::create
420 /// [`.truncate()`]: OpenOptions::truncate
421 ///
422 /// # Examples
423 ///
424 /// ```no_run
425 /// use tokio::fs::OpenOptions;
426 /// use std::io;
427 ///
428 /// #[tokio::main]
429 /// async fn main() -> io::Result<()> {
430 /// let file = OpenOptions::new()
431 /// .write(true)
432 /// .create_new(true)
433 /// .open("foo.txt")
434 /// .await?;
435 ///
436 /// Ok(())
437 /// }
438 /// ```
439 pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
440 match &mut self.inner {
441 Kind::Std(opts) => {
442 opts.create_new(create_new);
443 }
444 #[cfg(all(
445 tokio_unstable,
446 feature = "io-uring",
447 feature = "rt",
448 feature = "fs",
449 target_os = "linux"
450 ))]
451 Kind::Uring(opts) => {
452 opts.create_new(create_new);
453 }
454 }
455 self
456 }
457
458 /// Opens a file at `path` with the options specified by `self`.
459 ///
460 /// This is an async version of [`std::fs::OpenOptions::open`][std]
461 ///
462 /// [std]: std::fs::OpenOptions::open
463 ///
464 /// # Errors
465 ///
466 /// This function will return an error under a number of different
467 /// circumstances. Some of these error conditions are listed here, together
468 /// with their [`ErrorKind`]. The mapping to [`ErrorKind`]s is not part of
469 /// the compatibility contract of the function, especially the `Other` kind
470 /// might change to more specific kinds in the future.
471 ///
472 /// * [`NotFound`]: The specified file does not exist and neither `create`
473 /// or `create_new` is set.
474 /// * [`NotFound`]: One of the directory components of the file path does
475 /// not exist.
476 /// * [`PermissionDenied`]: The user lacks permission to get the specified
477 /// access rights for the file.
478 /// * [`PermissionDenied`]: The user lacks permission to open one of the
479 /// directory components of the specified path.
480 /// * [`AlreadyExists`]: `create_new` was specified and the file already
481 /// exists.
482 /// * [`InvalidInput`]: Invalid combinations of open options (truncate
483 /// without write access, no access mode set, etc.).
484 /// * [`Other`]: One of the directory components of the specified file path
485 /// was not, in fact, a directory.
486 /// * [`Other`]: Filesystem-level errors: full disk, write permission
487 /// requested on a read-only file system, exceeded disk quota, too many
488 /// open files, too long filename, too many symbolic links in the
489 /// specified path (Unix-like systems only), etc.
490 ///
491 /// # io_uring support
492 ///
493 /// On Linux, you can also use `io_uring` for executing system calls.
494 /// To enable `io_uring`, you need to specify the `--cfg tokio_unstable`
495 /// flag at compile time, enable the `io-uring` cargo feature, and set the
496 /// `Builder::enable_io_uring` runtime option.
497 ///
498 /// Support for `io_uring` is currently experimental, so its behavior may
499 /// change or it may be removed in future versions.
500 ///
501 /// # Examples
502 ///
503 /// ```no_run
504 /// use tokio::fs::OpenOptions;
505 /// use std::io;
506 ///
507 /// #[tokio::main]
508 /// async fn main() -> io::Result<()> {
509 /// let file = OpenOptions::new().open("foo.txt").await?;
510 /// Ok(())
511 /// }
512 /// ```
513 ///
514 /// [`ErrorKind`]: std::io::ErrorKind
515 /// [`AlreadyExists`]: std::io::ErrorKind::AlreadyExists
516 /// [`InvalidInput`]: std::io::ErrorKind::InvalidInput
517 /// [`NotFound`]: std::io::ErrorKind::NotFound
518 /// [`Other`]: std::io::ErrorKind::Other
519 /// [`PermissionDenied`]: std::io::ErrorKind::PermissionDenied
520 pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
521 match &self.inner {
522 Kind::Std(opts) => Self::std_open(opts, path).await,
523 #[cfg(all(
524 tokio_unstable,
525 feature = "io-uring",
526 feature = "rt",
527 feature = "fs",
528 target_os = "linux"
529 ))]
530 Kind::Uring(opts) => {
531 let handle = crate::runtime::Handle::current();
532 let driver_handle = handle.inner.driver().io();
533
534 if driver_handle.check_and_init()? {
535 Op::open(path.as_ref(), opts)?.await
536 } else {
537 let opts = opts.clone().into();
538 Self::std_open(&opts, path).await
539 }
540 }
541 }
542 }
543
544 async fn std_open(opts: &StdOpenOptions, path: impl AsRef<Path>) -> io::Result<File> {
545 let path = path.as_ref().to_owned();
546 let opts = opts.clone();
547
548 let std = asyncify(move || opts.open(path)).await?;
549 Ok(File::from_std(std))
550 }
551
552 #[cfg(windows)]
553 pub(super) fn as_inner_mut(&mut self) -> &mut StdOpenOptions {
554 match &mut self.inner {
555 Kind::Std(ref mut opts) => opts,
556 }
557 }
558}
559
560feature! {
561 #![unix]
562
563 impl OpenOptions {
564 /// Sets the mode bits that a new file will be created with.
565 ///
566 /// If a new file is created as part of an `OpenOptions::open` call then this
567 /// specified `mode` will be used as the permission bits for the new file.
568 /// If no `mode` is set, the default of `0o666` will be used.
569 /// The operating system masks out bits with the system's `umask`, to produce
570 /// the final permissions.
571 ///
572 /// # Examples
573 ///
574 /// ```no_run
575 /// use tokio::fs::OpenOptions;
576 /// use std::io;
577 ///
578 /// #[tokio::main]
579 /// async fn main() -> io::Result<()> {
580 /// let mut options = OpenOptions::new();
581 /// options.mode(0o644); // Give read/write for owner and read for others.
582 /// let file = options.open("foo.txt").await?;
583 ///
584 /// Ok(())
585 /// }
586 /// ```
587 pub fn mode(&mut self, mode: u32) -> &mut OpenOptions {
588 match &mut self.inner {
589 Kind::Std(opts) => {
590 opts.mode(mode);
591 }
592 #[cfg(all(
593 tokio_unstable,
594 feature = "io-uring",
595 feature = "rt",
596 feature = "fs",
597 target_os = "linux"
598 ))]
599 Kind::Uring(opts) => {
600 opts.mode(mode);
601 }
602 }
603 self
604 }
605
606 /// Passes custom flags to the `flags` argument of `open`.
607 ///
608 /// The bits that define the access mode are masked out with `O_ACCMODE`, to
609 /// ensure they do not interfere with the access mode set by Rusts options.
610 ///
611 /// Custom flags can only set flags, not remove flags set by Rusts options.
612 /// This options overwrites any previously set custom flags.
613 ///
614 /// # Examples
615 ///
616 /// ```no_run
617 /// use tokio::fs::OpenOptions;
618 /// use std::io;
619 ///
620 /// #[tokio::main]
621 /// async fn main() -> io::Result<()> {
622 /// let mut options = OpenOptions::new();
623 /// options.write(true);
624 /// if cfg!(unix) {
625 /// options.custom_flags(libc::O_NOFOLLOW);
626 /// }
627 /// let file = options.open("foo.txt").await?;
628 ///
629 /// Ok(())
630 /// }
631 /// ```
632 pub fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
633 match &mut self.inner {
634 Kind::Std(opts) => {
635 opts.custom_flags(flags);
636 }
637 #[cfg(all(
638 tokio_unstable,
639 feature = "io-uring",
640 feature = "rt",
641 feature = "fs",
642 target_os = "linux"
643 ))]
644 Kind::Uring(opts) => {
645 opts.custom_flags(flags);
646 }
647 }
648 self
649 }
650 }
651}
652
653cfg_windows! {
654 impl OpenOptions {
655 /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
656 /// with the specified value.
657 ///
658 /// This will override the `read`, `write`, and `append` flags on the
659 /// `OpenOptions` structure. This method provides fine-grained control over
660 /// the permissions to read, write and append data, attributes (like hidden
661 /// and system), and extended attributes.
662 ///
663 /// # Examples
664 ///
665 /// ```no_run
666 /// use tokio::fs::OpenOptions;
667 ///
668 /// # #[tokio::main]
669 /// # async fn main() -> std::io::Result<()> {
670 /// // Open without read and write permission, for example if you only need
671 /// // to call `stat` on the file
672 /// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?;
673 /// # Ok(())
674 /// # }
675 /// ```
676 ///
677 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
678 pub fn access_mode(&mut self, access: u32) -> &mut OpenOptions {
679 self.as_inner_mut().access_mode(access);
680 self
681 }
682
683 /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with
684 /// the specified value.
685 ///
686 /// By default `share_mode` is set to
687 /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows
688 /// other processes to read, write, and delete/rename the same file
689 /// while it is open. Removing any of the flags will prevent other
690 /// processes from performing the corresponding operation until the file
691 /// handle is closed.
692 ///
693 /// # Examples
694 ///
695 /// ```no_run
696 /// use tokio::fs::OpenOptions;
697 ///
698 /// # #[tokio::main]
699 /// # async fn main() -> std::io::Result<()> {
700 /// // Do not allow others to read or modify this file while we have it open
701 /// // for writing.
702 /// let file = OpenOptions::new()
703 /// .write(true)
704 /// .share_mode(0)
705 /// .open("foo.txt").await?;
706 /// # Ok(())
707 /// # }
708 /// ```
709 ///
710 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
711 pub fn share_mode(&mut self, share: u32) -> &mut OpenOptions {
712 self.as_inner_mut().share_mode(share);
713 self
714 }
715
716 /// Sets extra flags for the `dwFileFlags` argument to the call to
717 /// [`CreateFile2`] to the specified value (or combines it with
718 /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes`
719 /// for [`CreateFile`]).
720 ///
721 /// Custom flags can only set flags, not remove flags set by Rust's options.
722 /// This option overwrites any previously set custom flags.
723 ///
724 /// # Examples
725 ///
726 /// ```no_run
727 /// use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE;
728 /// use tokio::fs::OpenOptions;
729 ///
730 /// # #[tokio::main]
731 /// # async fn main() -> std::io::Result<()> {
732 /// let file = OpenOptions::new()
733 /// .create(true)
734 /// .write(true)
735 /// .custom_flags(FILE_FLAG_DELETE_ON_CLOSE)
736 /// .open("foo.txt").await?;
737 /// # Ok(())
738 /// # }
739 /// ```
740 ///
741 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
742 /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
743 pub fn custom_flags(&mut self, flags: u32) -> &mut OpenOptions {
744 self.as_inner_mut().custom_flags(flags);
745 self
746 }
747
748 /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to
749 /// the specified value (or combines it with `custom_flags` and
750 /// `security_qos_flags` to set the `dwFlagsAndAttributes` for
751 /// [`CreateFile`]).
752 ///
753 /// If a _new_ file is created because it does not yet exist and
754 /// `.create(true)` or `.create_new(true)` are specified, the new file is
755 /// given the attributes declared with `.attributes()`.
756 ///
757 /// If an _existing_ file is opened with `.create(true).truncate(true)`, its
758 /// existing attributes are preserved and combined with the ones declared
759 /// with `.attributes()`.
760 ///
761 /// In all other cases the attributes get ignored.
762 ///
763 /// # Examples
764 ///
765 /// ```no_run
766 /// use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN;
767 /// use tokio::fs::OpenOptions;
768 ///
769 /// # #[tokio::main]
770 /// # async fn main() -> std::io::Result<()> {
771 /// let file = OpenOptions::new()
772 /// .write(true)
773 /// .create(true)
774 /// .attributes(FILE_ATTRIBUTE_HIDDEN)
775 /// .open("foo.txt").await?;
776 /// # Ok(())
777 /// # }
778 /// ```
779 ///
780 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
781 /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
782 pub fn attributes(&mut self, attributes: u32) -> &mut OpenOptions {
783 self.as_inner_mut().attributes(attributes);
784 self
785 }
786
787 /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to
788 /// the specified value (or combines it with `custom_flags` and `attributes`
789 /// to set the `dwFlagsAndAttributes` for [`CreateFile`]).
790 ///
791 /// By default `security_qos_flags` is not set. It should be specified when
792 /// opening a named pipe, to control to which degree a server process can
793 /// act on behalf of a client process (security impersonation level).
794 ///
795 /// When `security_qos_flags` is not set, a malicious program can gain the
796 /// elevated privileges of a privileged Rust process when it allows opening
797 /// user-specified paths, by tricking it into opening a named pipe. So
798 /// arguably `security_qos_flags` should also be set when opening arbitrary
799 /// paths. However the bits can then conflict with other flags, specifically
800 /// `FILE_FLAG_OPEN_NO_RECALL`.
801 ///
802 /// For information about possible values, see [Impersonation Levels] on the
803 /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set
804 /// automatically when using this method.
805 ///
806 /// # Examples
807 ///
808 /// ```no_run
809 /// use windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION;
810 /// use tokio::fs::OpenOptions;
811 ///
812 /// # #[tokio::main]
813 /// # async fn main() -> std::io::Result<()> {
814 /// let file = OpenOptions::new()
815 /// .write(true)
816 /// .create(true)
817 ///
818 /// // Sets the flag value to `SecurityIdentification`.
819 /// .security_qos_flags(SECURITY_IDENTIFICATION)
820 ///
821 /// .open(r"\\.\pipe\MyPipe").await?;
822 /// # Ok(())
823 /// # }
824 /// ```
825 ///
826 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
827 /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
828 /// [Impersonation Levels]:
829 /// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
830 pub fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions {
831 self.as_inner_mut().security_qos_flags(flags);
832 self
833 }
834 }
835}
836
837impl From<StdOpenOptions> for OpenOptions {
838 fn from(options: StdOpenOptions) -> OpenOptions {
839 OpenOptions {
840 inner: Kind::Std(options),
841 // TODO: Add support for converting `StdOpenOptions` to `UringOpenOptions`
842 // if user enables `io-uring` cargo feature. It is blocked by:
843 // * https://github.com/rust-lang/rust/issues/74943
844 // * https://github.com/rust-lang/rust/issues/76801
845 }
846 }
847}
848
849impl Default for OpenOptions {
850 fn default() -> Self {
851 Self::new()
852 }
853}