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