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