Skip to content

Commit

Permalink
Add OpenOptionsExt for Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
vlovich committed Jan 30, 2021
1 parent 09f2c5f commit 0c35de1
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 0 deletions.
36 changes: 36 additions & 0 deletions src/fs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,7 @@ impl LockGuard<State> {
#[cfg(test)]
mod tests {
use super::*;
use crate::fs::OpenOptions;

#[test]
fn async_file_drop() {
Expand All @@ -903,4 +904,39 @@ mod tests {
assert_eq!(len as u64, file.metadata().await.unwrap().len());
});
}

#[cfg(target_os = "windows")]
#[test]
fn async_file_win_openext() {
use super::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::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();
});
}
}
33 changes: 33 additions & 0 deletions src/fs/open_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,36 @@ cfg_unix! {
}
}
}

cfg_unstable_default! {
cfg_windows! {
use crate::os::windows::fs::OpenOptionsExt;

impl OpenOptionsExt 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
}
}
}
}
175 changes: 175 additions & 0 deletions src/os/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,178 @@ pub async fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(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
#[stable(feature = "open_options_ext", since = "1.10.0")]
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
#[stable(feature = "open_options_ext", since = "1.10.0")]
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
#[stable(feature = "open_options_ext", since = "1.10.0")]
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
#[stable(feature = "open_options_ext", since = "1.10.0")]
fn security_qos_flags(&mut self, flags: u32) -> &mut Self;
}
}

0 comments on commit 0c35de1

Please sign in to comment.