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