madsim_real_tokio/fs/
open_options.rs

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