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}
663