Skip to main content

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
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        self.open_inner(path.as_ref()).await
522    }
523
524    async fn open_inner(&self, path: &Path) -> io::Result<File> {
525        match &self.inner {
526            Kind::Std(opts) => Self::std_open(opts, path).await,
527            #[cfg(all(
528                tokio_unstable,
529                feature = "io-uring",
530                feature = "rt",
531                feature = "fs",
532                target_os = "linux"
533            ))]
534            Kind::Uring(opts) => {
535                if let Ok(handle) = crate::runtime::Handle::try_current() {
536                    if let Some(driver_handle) = handle.inner.driver().io.as_ref() {
537                        if driver_handle
538                            .check_and_init(io_uring::opcode::OpenAt::CODE)
539                            .await?
540                        {
541                            return crate::io::uring::open::open(path, opts).await;
542                        }
543                    }
544                }
545
546                let opts = opts.clone().into();
547                Self::std_open(&opts, path).await
548            }
549        }
550    }
551
552    async fn std_open(opts: &StdOpenOptions, path: &Path) -> io::Result<File> {
553        let path = path.to_owned();
554        let opts = opts.clone();
555
556        Ok(asyncify(move || opts.open(path)).await?.into())
557    }
558
559    #[cfg(windows)]
560    pub(super) fn as_inner_mut(&mut self) -> &mut StdOpenOptions {
561        match &mut self.inner {
562            Kind::Std(ref mut opts) => opts,
563        }
564    }
565}
566
567feature! {
568    #![unix]
569
570    impl OpenOptions {
571        /// Sets the mode bits that a new file will be created with.
572        ///
573        /// If a new file is created as part of an `OpenOptions::open` call then this
574        /// specified `mode` will be used as the permission bits for the new file.
575        /// If no `mode` is set, the default of `0o666` will be used.
576        /// The operating system masks out bits with the system's `umask`, to produce
577        /// the final permissions.
578        ///
579        /// # Examples
580        ///
581        /// ```no_run
582        /// use tokio::fs::OpenOptions;
583        /// use std::io;
584        ///
585        /// #[tokio::main]
586        /// async fn main() -> io::Result<()> {
587        ///     let mut options = OpenOptions::new();
588        ///     options.mode(0o644); // Give read/write for owner and read for others.
589        ///     let file = options.open("foo.txt").await?;
590        ///
591        ///     Ok(())
592        /// }
593        /// ```
594        pub fn mode(&mut self, mode: u32) -> &mut OpenOptions {
595            match &mut self.inner {
596                Kind::Std(opts) => {
597                    opts.mode(mode);
598                }
599                #[cfg(all(
600                    tokio_unstable,
601                    feature = "io-uring",
602                    feature = "rt",
603                    feature = "fs",
604                    target_os = "linux"
605                ))]
606                Kind::Uring(opts) => {
607                    opts.mode(mode);
608                }
609            }
610            self
611        }
612
613        /// Passes custom flags to the `flags` argument of `open`.
614        ///
615        /// The bits that define the access mode are masked out with `O_ACCMODE`, to
616        /// ensure they do not interfere with the access mode set by Rusts options.
617        ///
618        /// Custom flags can only set flags, not remove flags set by Rusts options.
619        /// This options overwrites any previously set custom flags.
620        ///
621        /// # Examples
622        ///
623        /// ```no_run
624        /// use tokio::fs::OpenOptions;
625        /// use std::io;
626        ///
627        /// #[tokio::main]
628        /// async fn main() -> io::Result<()> {
629        ///     let mut options = OpenOptions::new();
630        ///     options.write(true);
631        ///     if cfg!(unix) {
632        ///         options.custom_flags(libc::O_NOFOLLOW);
633        ///     }
634        ///     let file = options.open("foo.txt").await?;
635        ///
636        ///     Ok(())
637        /// }
638        /// ```
639        pub fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
640            match &mut self.inner {
641                Kind::Std(opts) => {
642                    opts.custom_flags(flags);
643                }
644                #[cfg(all(
645                    tokio_unstable,
646                    feature = "io-uring",
647                    feature = "rt",
648                    feature = "fs",
649                    target_os = "linux"
650                ))]
651                Kind::Uring(opts) => {
652                    opts.custom_flags(flags);
653                }
654            }
655            self
656        }
657    }
658}
659
660cfg_windows! {
661    impl OpenOptions {
662        /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
663        /// with the specified value.
664        ///
665        /// This will override the `read`, `write`, and `append` flags on the
666        /// `OpenOptions` structure. This method provides fine-grained control over
667        /// the permissions to read, write and append data, attributes (like hidden
668        /// and system), and extended attributes.
669        ///
670        /// # Examples
671        ///
672        /// ```no_run
673        /// use tokio::fs::OpenOptions;
674        ///
675        /// # #[tokio::main]
676        /// # async fn main() -> std::io::Result<()> {
677        /// // Open without read and write permission, for example if you only need
678        /// // to call `stat` on the file
679        /// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?;
680        /// # Ok(())
681        /// # }
682        /// ```
683        ///
684        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
685        pub fn access_mode(&mut self, access: u32) -> &mut OpenOptions {
686            self.as_inner_mut().access_mode(access);
687            self
688        }
689
690        /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with
691        /// the specified value.
692        ///
693        /// By default `share_mode` is set to
694        /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows
695        /// other processes to read, write, and delete/rename the same file
696        /// while it is open. Removing any of the flags will prevent other
697        /// processes from performing the corresponding operation until the file
698        /// handle is closed.
699        ///
700        /// # Examples
701        ///
702        /// ```no_run
703        /// use tokio::fs::OpenOptions;
704        ///
705        /// # #[tokio::main]
706        /// # async fn main() -> std::io::Result<()> {
707        /// // Do not allow others to read or modify this file while we have it open
708        /// // for writing.
709        /// let file = OpenOptions::new()
710        ///     .write(true)
711        ///     .share_mode(0)
712        ///     .open("foo.txt").await?;
713        /// # Ok(())
714        /// # }
715        /// ```
716        ///
717        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
718        pub fn share_mode(&mut self, share: u32) -> &mut OpenOptions {
719            self.as_inner_mut().share_mode(share);
720            self
721        }
722
723        /// Sets extra flags for the `dwFileFlags` argument to the call to
724        /// [`CreateFile2`] to the specified value (or combines it with
725        /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes`
726        /// for [`CreateFile`]).
727        ///
728        /// Custom flags can only set flags, not remove flags set by Rust's options.
729        /// This option overwrites any previously set custom flags.
730        ///
731        /// # Examples
732        ///
733        /// ```no_run
734        /// use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE;
735        /// use tokio::fs::OpenOptions;
736        ///
737        /// # #[tokio::main]
738        /// # async fn main() -> std::io::Result<()> {
739        /// let file = OpenOptions::new()
740        ///     .create(true)
741        ///     .write(true)
742        ///     .custom_flags(FILE_FLAG_DELETE_ON_CLOSE)
743        ///     .open("foo.txt").await?;
744        /// # Ok(())
745        /// # }
746        /// ```
747        ///
748        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
749        /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
750        pub fn custom_flags(&mut self, flags: u32) -> &mut OpenOptions {
751            self.as_inner_mut().custom_flags(flags);
752            self
753        }
754
755        /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to
756        /// the specified value (or combines it with `custom_flags` and
757        /// `security_qos_flags` to set the `dwFlagsAndAttributes` for
758        /// [`CreateFile`]).
759        ///
760        /// If a _new_ file is created because it does not yet exist and
761        /// `.create(true)` or `.create_new(true)` are specified, the new file is
762        /// given the attributes declared with `.attributes()`.
763        ///
764        /// If an _existing_ file is opened with `.create(true).truncate(true)`, its
765        /// existing attributes are preserved and combined with the ones declared
766        /// with `.attributes()`.
767        ///
768        /// In all other cases the attributes get ignored.
769        ///
770        /// # Examples
771        ///
772        /// ```no_run
773        /// use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN;
774        /// use tokio::fs::OpenOptions;
775        ///
776        /// # #[tokio::main]
777        /// # async fn main() -> std::io::Result<()> {
778        /// let file = OpenOptions::new()
779        ///     .write(true)
780        ///     .create(true)
781        ///     .attributes(FILE_ATTRIBUTE_HIDDEN)
782        ///     .open("foo.txt").await?;
783        /// # Ok(())
784        /// # }
785        /// ```
786        ///
787        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
788        /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
789        pub fn attributes(&mut self, attributes: u32) -> &mut OpenOptions {
790            self.as_inner_mut().attributes(attributes);
791            self
792        }
793
794        /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to
795        /// the specified value (or combines it with `custom_flags` and `attributes`
796        /// to set the `dwFlagsAndAttributes` for [`CreateFile`]).
797        ///
798        /// By default `security_qos_flags` is not set. It should be specified when
799        /// opening a named pipe, to control to which degree a server process can
800        /// act on behalf of a client process (security impersonation level).
801        ///
802        /// When `security_qos_flags` is not set, a malicious program can gain the
803        /// elevated privileges of a privileged Rust process when it allows opening
804        /// user-specified paths, by tricking it into opening a named pipe. So
805        /// arguably `security_qos_flags` should also be set when opening arbitrary
806        /// paths. However the bits can then conflict with other flags, specifically
807        /// `FILE_FLAG_OPEN_NO_RECALL`.
808        ///
809        /// For information about possible values, see [Impersonation Levels] on the
810        /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set
811        /// automatically when using this method.
812        ///
813        /// # Examples
814        ///
815        /// ```no_run
816        /// use windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION;
817        /// use tokio::fs::OpenOptions;
818        ///
819        /// # #[tokio::main]
820        /// # async fn main() -> std::io::Result<()> {
821        /// let file = OpenOptions::new()
822        ///     .write(true)
823        ///     .create(true)
824        ///
825        ///     // Sets the flag value to `SecurityIdentification`.
826        ///     .security_qos_flags(SECURITY_IDENTIFICATION)
827        ///
828        ///     .open(r"\\.\pipe\MyPipe").await?;
829        /// # Ok(())
830        /// # }
831        /// ```
832        ///
833        /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
834        /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
835        /// [Impersonation Levels]:
836        ///     https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
837        pub fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions {
838            self.as_inner_mut().security_qos_flags(flags);
839            self
840        }
841    }
842}
843
844impl From<StdOpenOptions> for OpenOptions {
845    fn from(options: StdOpenOptions) -> OpenOptions {
846        OpenOptions {
847            inner: Kind::Std(options),
848            // TODO: Add support for converting `StdOpenOptions` to `UringOpenOptions`
849            // if user enables `io-uring` cargo feature. It is blocked by:
850            // * https://github.com/rust-lang/rust/issues/74943
851            // * https://github.com/rust-lang/rust/issues/76801
852        }
853    }
854}
855
856impl Default for OpenOptions {
857    fn default() -> Self {
858        Self::new()
859    }
860}