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