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 #[cfg(unix)] 14 use std::os::unix::fs::OpenOptionsExt; 15 #[cfg(windows)] 16 use 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)] 82 pub struct OpenOptions(StdOpenOptions); 83 84 impl 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 /// ``` new() -> OpenOptions101 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 /// ``` read(&mut self, read: bool) -> &mut OpenOptions130 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 /// ``` write(&mut self, write: bool) -> &mut OpenOptions160 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 /// ``` append(&mut self, append: bool) -> &mut OpenOptions219 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 /// ``` truncate(&mut self, truncate: bool) -> &mut OpenOptions252 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 /// ``` create(&mut self, create: bool) -> &mut OpenOptions288 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 /// ``` create_new(&mut self, create_new: bool) -> &mut OpenOptions331 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 open(&self, path: impl AsRef<Path>) -> io::Result<File>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 #[cfg(any(windows, unix))] as_inner_mut(&mut self) -> &mut StdOpenOptions398 pub(super) fn as_inner_mut(&mut self) -> &mut StdOpenOptions { 399 &mut self.0 400 } 401 } 402 403 feature! { 404 #![unix] 405 406 impl OpenOptions { 407 /// Sets the mode bits that a new file will be created with. 408 /// 409 /// If a new file is created as part of an `OpenOptions::open` call then this 410 /// specified `mode` will be used as the permission bits for the new file. 411 /// If no `mode` is set, the default of `0o666` will be used. 412 /// The operating system masks out bits with the system's `umask`, to produce 413 /// the final permissions. 414 /// 415 /// # Examples 416 /// 417 /// ```no_run 418 /// use tokio::fs::OpenOptions; 419 /// use std::io; 420 /// 421 /// #[tokio::main] 422 /// async fn main() -> io::Result<()> { 423 /// let mut options = OpenOptions::new(); 424 /// options.mode(0o644); // Give read/write for owner and read for others. 425 /// let file = options.open("foo.txt").await?; 426 /// 427 /// Ok(()) 428 /// } 429 /// ``` 430 pub fn mode(&mut self, mode: u32) -> &mut OpenOptions { 431 self.as_inner_mut().mode(mode); 432 self 433 } 434 435 /// Passes custom flags to the `flags` argument of `open`. 436 /// 437 /// The bits that define the access mode are masked out with `O_ACCMODE`, to 438 /// ensure they do not interfere with the access mode set by Rusts options. 439 /// 440 /// Custom flags can only set flags, not remove flags set by Rusts options. 441 /// This options overwrites any previously set custom flags. 442 /// 443 /// # Examples 444 /// 445 /// ```no_run 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 468 cfg_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 652 impl From<StdOpenOptions> for OpenOptions { from(options: StdOpenOptions) -> OpenOptions653 fn from(options: StdOpenOptions) -> OpenOptions { 654 OpenOptions(options) 655 } 656 } 657 658 impl Default for OpenOptions { default() -> Self659 fn default() -> Self { 660 Self::new() 661 } 662 } 663