diff --git a/src/fs/file.rs b/src/fs/file.rs index e8cad7ad7..7c6d41e25 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -903,4 +903,44 @@ mod tests { assert_eq!(len as u64, file.metadata().await.unwrap().len()); }); } + + cfg_unstable! { + cfg_windows! { + #[test] + fn async_file_win_openext() { + use super::fs::OpenOptions; + use crate::os::windows::fs::OpenOptionsExt; + const FILE_FLAG_NO_BUFFERING: u32 = 0x2000_0000; + const FILE_FLAG_RANDOM_ACCESS: u32 = 0x1000_0000; + + crate::task::block_on(async move { + OpenOptions::new() + .read(true) + .write(true) + .create_new(true) + .custom_flags(FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS) + .open(file!()).await.unwrap(); + }); + } + } + } + + #[cfg(target_os = "unix")] + #[test] + fn async_file_unix_openext() { + use super::fs::OpenOptions; + use crate::os::unix::fs::OpenOptionsExt; + const O_DIRECT: i32 = 0o0_040_000; + + crate::task::block_on(async move { + OpenOptions::new() + .read(true) + .write(true) + .create_new(true) + .custom_flags(O_DIRECT) + .open(file!()) + .await + .unwrap(); + }); + } } diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index 91ad8cab5..9e310e4f1 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -298,9 +298,9 @@ impl Default for OpenOptions { } cfg_unix! { - use crate::os::unix::fs::OpenOptionsExt; + use crate::os::unix::fs::OpenOptionsExt as UnixOpenOptionsExt; - impl OpenOptionsExt for OpenOptions { + impl UnixOpenOptionsExt for OpenOptions { fn mode(&mut self, mode: u32) -> &mut Self { self.0.mode(mode); self @@ -312,3 +312,36 @@ cfg_unix! { } } } + +cfg_unstable_default! { +cfg_windows! { + use crate::os::windows::fs::OpenOptionsExt as WindowsOpenOptionsExt; + + impl WindowsOpenOptionsExt for OpenOptions { + fn access_mode(&mut self, access: u32) -> &mut OpenOptions { + self.0.access_mode(access); + self + } + + fn share_mode(&mut self, share: u32) -> &mut OpenOptions { + self.0.share_mode(share); + self + } + + fn custom_flags(&mut self, flags: u32) -> &mut OpenOptions { + self.0.custom_flags(flags); + self + } + + fn attributes(&mut self, attributes: u32) -> &mut OpenOptions { + self.0.attributes(attributes); + self + } + + fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions { + self.0.security_qos_flags(flags); + self + } + } +} +} diff --git a/src/os/windows/fs.rs b/src/os/windows/fs.rs index 243f3819d..3e2ea47a9 100644 --- a/src/os/windows/fs.rs +++ b/src/os/windows/fs.rs @@ -53,3 +53,174 @@ pub async fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io: let dst = dst.as_ref().to_owned(); spawn_blocking(move || std::os::windows::fs::symlink_file(&src, &dst)).await } + +cfg_not_docs! { + pub use std::os::windows::fs::{OpenOptionsExt}; +} + +cfg_docs! { + /// Windows-specific extensions to `OpenOptions`. + pub trait OpenOptionsExt { + /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] + /// with the specified value. + /// + /// This will override the `read`, `write`, and `append` flags on the + /// `OpenOptions` structure. This method provides fine-grained control over + /// the permissions to read, write and append data, attributes (like hidden + /// and system), and extended attributes. + /// + /// # Examples + /// + /// ```no_run + /// use async_std::fs::OpenOptions; + /// use async_std::os::windows::prelude::*; + /// + /// // Open without read and write permission, for example if you only need + /// // to call `stat` on the file + /// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?; + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + fn access_mode(&mut self, access: u32) -> &mut Self; + + /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with + /// the specified value. + /// + /// By default `share_mode` is set to + /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows + /// other processes to read, write, and delete/rename the same file + /// while it is open. Removing any of the flags will prevent other + /// processes from performing the corresponding operation until the file + /// handle is closed. + /// + /// # Examples + /// + /// ```no_run + /// use async_std::fs::OpenOptions; + /// use async_std::os::windows::prelude::*; + /// + /// // Do not allow others to read or modify this file while we have it open + /// // for writing. + /// let file = OpenOptions::new() + /// .write(true) + /// .share_mode(0) + /// .open("foo.txt") + /// .await?; + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + fn share_mode(&mut self, val: u32) -> &mut Self; + + /// Sets extra flags for the `dwFileFlags` argument to the call to + /// [`CreateFile2`] to the specified value (or combines it with + /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes` + /// for [`CreateFile`]). + /// + /// Custom flags can only set flags, not remove flags set by Rust's options. + /// This option overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```no_run + /// # #[cfg(for_demonstration_only)] + /// extern crate winapi; + /// # mod winapi { pub const FILE_FLAG_DELETE_ON_CLOSE: u32 = 0x04000000; } + /// + /// use async_std::fs::OpenOptions; + /// use async_std::os::windows::prelude::*; + /// + /// let file = OpenOptions::new() + /// .create(true) + /// .write(true) + /// .custom_flags(winapi::FILE_FLAG_DELETE_ON_CLOSE) + /// .open("foo.txt") + /// .await?; + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + fn custom_flags(&mut self, flags: u32) -> &mut Self; + + /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and + /// `security_qos_flags` to set the `dwFlagsAndAttributes` for + /// [`CreateFile`]). + /// + /// If a _new_ file is created because it does not yet exist and + /// `.create(true)` or `.create_new(true)` are specified, the new file is + /// given the attributes declared with `.attributes()`. + /// + /// If an _existing_ file is opened with `.create(true).truncate(true)`, its + /// existing attributes are preserved and combined with the ones declared + /// with `.attributes()`. + /// + /// In all other cases the attributes get ignored. + /// + /// # Examples + /// + /// ```no_run + /// # #[cfg(for_demonstration_only)] + /// extern crate winapi; + /// # mod winapi { pub const FILE_ATTRIBUTE_HIDDEN: u32 = 2; } + /// + /// use async_std::fs::OpenOptions; + /// use async_std::os::windows::prelude::*; + /// + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// .attributes(winapi::FILE_ATTRIBUTE_HIDDEN) + /// .open("foo.txt") + /// .await?; + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + fn attributes(&mut self, val: u32) -> &mut Self; + + /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and `attributes` + /// to set the `dwFlagsAndAttributes` for [`CreateFile`]). + /// + /// By default `security_qos_flags` is not set. It should be specified when + /// opening a named pipe, to control to which degree a server process can + /// act on behalf of a client process (security impersonation level). + /// + /// When `security_qos_flags` is not set, a malicious program can gain the + /// elevated privileges of a privileged Rust process when it allows opening + /// user-specified paths, by tricking it into opening a named pipe. So + /// arguably `security_qos_flags` should also be set when opening arbitrary + /// paths. However the bits can then conflict with other flags, specifically + /// `FILE_FLAG_OPEN_NO_RECALL`. + /// + /// For information about possible values, see [Impersonation Levels] on the + /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set + /// automatically when using this method. + + /// # Examples + /// + /// ```no_run + /// # #[cfg(for_demonstration_only)] + /// extern crate winapi; + /// # mod winapi { pub const SECURITY_IDENTIFICATION: u32 = 0; } + /// use async_std::fs::OpenOptions; + /// use async_std::os::windows::prelude::*; + /// + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// + /// // Sets the flag value to `SecurityIdentification`. + /// .security_qos_flags(winapi::SECURITY_IDENTIFICATION) + /// + /// .open(r"\\.\pipe\MyPipe") + /// .await?; + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + /// [Impersonation Levels]: + /// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level + fn security_qos_flags(&mut self, flags: u32) -> &mut Self; + } +}