Skip to content

Commit 545dbd3

Browse files
committed
Add OpenOptionsExt for Windows
1 parent 09f2c5f commit 545dbd3

File tree

3 files changed

+241
-2
lines changed

3 files changed

+241
-2
lines changed

src/fs/file.rs

+35
Original file line numberDiff line numberDiff line change
@@ -903,4 +903,39 @@ mod tests {
903903
assert_eq!(len as u64, file.metadata().await.unwrap().len());
904904
});
905905
}
906+
907+
#[cfg(all(target_os = "windows", feature = "unstable"))]
908+
#[test]
909+
fn async_file_win_openext() {
910+
use super::os::windows::fs::OpenOptionsExt;
911+
const FILE_FLAG_NO_BUFFERING: u32 = 0x2000_0000;
912+
const FILE_FLAG_RANDOM_ACCESS: u32 = 0x1000_0000;
913+
914+
crate::task::block_on(async move {
915+
OpenOptions::new()
916+
.read(true)
917+
.write(true)
918+
.create_new(true)
919+
.custom_flags(FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS)
920+
.open(file!()).await.unwrap();
921+
});
922+
}
923+
924+
#[cfg(target_os = "unix")]
925+
#[test]
926+
fn async_file_unix_openext() {
927+
use super::os::unix::fs::OpenOptionsExt;
928+
const O_DIRECT: i32 = 0o0_040_000;
929+
930+
crate::task::block_on(async move {
931+
OpenOptions::new()
932+
.read(true)
933+
.write(true)
934+
.create_new(true)
935+
.custom_flags(O_DIRECT)
936+
.open(file!())
937+
.await
938+
.unwrap();
939+
});
940+
}
906941
}

src/fs/open_options.rs

+35-2
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,9 @@ impl Default for OpenOptions {
298298
}
299299

300300
cfg_unix! {
301-
use crate::os::unix::fs::OpenOptionsExt;
301+
use crate::os::unix::fs::OpenOptionsExt as UnixOpenOptionsExt;
302302

303-
impl OpenOptionsExt for OpenOptions {
303+
impl UnixOpenOptionsExt for OpenOptions {
304304
fn mode(&mut self, mode: u32) -> &mut Self {
305305
self.0.mode(mode);
306306
self
@@ -312,3 +312,36 @@ cfg_unix! {
312312
}
313313
}
314314
}
315+
316+
cfg_unstable_default! {
317+
cfg_windows! {
318+
use crate::os::windows::fs::OpenOptionsExt as WindowsOpenOptionsExt;
319+
320+
impl WindowsOpenOptionsExt for OpenOptions {
321+
fn access_mode(&mut self, access: u32) -> &mut OpenOptions {
322+
self.0.access_mode(access);
323+
self
324+
}
325+
326+
fn share_mode(&mut self, share: u32) -> &mut OpenOptions {
327+
self.0.share_mode(share);
328+
self
329+
}
330+
331+
fn custom_flags(&mut self, flags: u32) -> &mut OpenOptions {
332+
self.0.custom_flags(flags);
333+
self
334+
}
335+
336+
fn attributes(&mut self, attributes: u32) -> &mut OpenOptions {
337+
self.0.attributes(attributes);
338+
self
339+
}
340+
341+
fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions {
342+
self.0.security_qos_flags(flags);
343+
self
344+
}
345+
}
346+
}
347+
}

src/os/windows/fs.rs

+171
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,174 @@ pub async fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io:
5353
let dst = dst.as_ref().to_owned();
5454
spawn_blocking(move || std::os::windows::fs::symlink_file(&src, &dst)).await
5555
}
56+
57+
cfg_not_docs! {
58+
pub use std::os::windows::fs::{OpenOptionsExt};
59+
}
60+
61+
cfg_docs! {
62+
/// Windows-specific extensions to `OpenOptions`.
63+
pub trait OpenOptionsExt {
64+
/// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
65+
/// with the specified value.
66+
///
67+
/// This will override the `read`, `write`, and `append` flags on the
68+
/// `OpenOptions` structure. This method provides fine-grained control over
69+
/// the permissions to read, write and append data, attributes (like hidden
70+
/// and system), and extended attributes.
71+
///
72+
/// # Examples
73+
///
74+
/// ```no_run
75+
/// use async_std::fs::OpenOptions;
76+
/// use async_std::os::windows::prelude::*;
77+
///
78+
/// // Open without read and write permission, for example if you only need
79+
/// // to call `stat` on the file
80+
/// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?;
81+
/// ```
82+
///
83+
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
84+
fn access_mode(&mut self, access: u32) -> &mut Self;
85+
86+
/// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with
87+
/// the specified value.
88+
///
89+
/// By default `share_mode` is set to
90+
/// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows
91+
/// other processes to read, write, and delete/rename the same file
92+
/// while it is open. Removing any of the flags will prevent other
93+
/// processes from performing the corresponding operation until the file
94+
/// handle is closed.
95+
///
96+
/// # Examples
97+
///
98+
/// ```no_run
99+
/// use async_std::fs::OpenOptions;
100+
/// use async_std::os::windows::prelude::*;
101+
///
102+
/// // Do not allow others to read or modify this file while we have it open
103+
/// // for writing.
104+
/// let file = OpenOptions::new()
105+
/// .write(true)
106+
/// .share_mode(0)
107+
/// .open("foo.txt")
108+
/// .await?;
109+
/// ```
110+
///
111+
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
112+
fn share_mode(&mut self, val: u32) -> &mut Self;
113+
114+
/// Sets extra flags for the `dwFileFlags` argument to the call to
115+
/// [`CreateFile2`] to the specified value (or combines it with
116+
/// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes`
117+
/// for [`CreateFile`]).
118+
///
119+
/// Custom flags can only set flags, not remove flags set by Rust's options.
120+
/// This option overwrites any previously set custom flags.
121+
///
122+
/// # Examples
123+
///
124+
/// ```no_run
125+
/// # #[cfg(for_demonstration_only)]
126+
/// extern crate winapi;
127+
/// # mod winapi { pub const FILE_FLAG_DELETE_ON_CLOSE: u32 = 0x04000000; }
128+
///
129+
/// use async_std::fs::OpenOptions;
130+
/// use async_std::os::windows::prelude::*;
131+
///
132+
/// let file = OpenOptions::new()
133+
/// .create(true)
134+
/// .write(true)
135+
/// .custom_flags(winapi::FILE_FLAG_DELETE_ON_CLOSE)
136+
/// .open("foo.txt")
137+
/// .await?;
138+
/// ```
139+
///
140+
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
141+
/// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
142+
fn custom_flags(&mut self, flags: u32) -> &mut Self;
143+
144+
/// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to
145+
/// the specified value (or combines it with `custom_flags` and
146+
/// `security_qos_flags` to set the `dwFlagsAndAttributes` for
147+
/// [`CreateFile`]).
148+
///
149+
/// If a _new_ file is created because it does not yet exist and
150+
/// `.create(true)` or `.create_new(true)` are specified, the new file is
151+
/// given the attributes declared with `.attributes()`.
152+
///
153+
/// If an _existing_ file is opened with `.create(true).truncate(true)`, its
154+
/// existing attributes are preserved and combined with the ones declared
155+
/// with `.attributes()`.
156+
///
157+
/// In all other cases the attributes get ignored.
158+
///
159+
/// # Examples
160+
///
161+
/// ```no_run
162+
/// # #[cfg(for_demonstration_only)]
163+
/// extern crate winapi;
164+
/// # mod winapi { pub const FILE_ATTRIBUTE_HIDDEN: u32 = 2; }
165+
///
166+
/// use async_std::fs::OpenOptions;
167+
/// use async_std::os::windows::prelude::*;
168+
///
169+
/// let file = OpenOptions::new()
170+
/// .write(true)
171+
/// .create(true)
172+
/// .attributes(winapi::FILE_ATTRIBUTE_HIDDEN)
173+
/// .open("foo.txt")
174+
/// .await?;
175+
/// ```
176+
///
177+
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
178+
/// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
179+
fn attributes(&mut self, val: u32) -> &mut Self;
180+
181+
/// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to
182+
/// the specified value (or combines it with `custom_flags` and `attributes`
183+
/// to set the `dwFlagsAndAttributes` for [`CreateFile`]).
184+
///
185+
/// By default `security_qos_flags` is not set. It should be specified when
186+
/// opening a named pipe, to control to which degree a server process can
187+
/// act on behalf of a client process (security impersonation level).
188+
///
189+
/// When `security_qos_flags` is not set, a malicious program can gain the
190+
/// elevated privileges of a privileged Rust process when it allows opening
191+
/// user-specified paths, by tricking it into opening a named pipe. So
192+
/// arguably `security_qos_flags` should also be set when opening arbitrary
193+
/// paths. However the bits can then conflict with other flags, specifically
194+
/// `FILE_FLAG_OPEN_NO_RECALL`.
195+
///
196+
/// For information about possible values, see [Impersonation Levels] on the
197+
/// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set
198+
/// automatically when using this method.
199+
200+
/// # Examples
201+
///
202+
/// ```no_run
203+
/// # #[cfg(for_demonstration_only)]
204+
/// extern crate winapi;
205+
/// # mod winapi { pub const SECURITY_IDENTIFICATION: u32 = 0; }
206+
/// use async_std::fs::OpenOptions;
207+
/// use async_std::os::windows::prelude::*;
208+
///
209+
/// let file = OpenOptions::new()
210+
/// .write(true)
211+
/// .create(true)
212+
///
213+
/// // Sets the flag value to `SecurityIdentification`.
214+
/// .security_qos_flags(winapi::SECURITY_IDENTIFICATION)
215+
///
216+
/// .open(r"\\.\pipe\MyPipe")
217+
/// .await?;
218+
/// ```
219+
///
220+
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
221+
/// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
222+
/// [Impersonation Levels]:
223+
/// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
224+
fn security_qos_flags(&mut self, flags: u32) -> &mut Self;
225+
}
226+
}

0 commit comments

Comments
 (0)