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