windows_namespace_request_sys/reopen.rs
1// Copyright (c) Mike Grier.
2
3//! The `ReOpenFile` entry.
4//!
5//! One entry for one call (D-3): a **new handle to the same file, directory or
6//! volume** a handle already names, with access, share mode and flags that the caller states anew.
7//! It is a third open primitive beside [`crate::open::OpenFile`] and
8//! [`crate::open_by_id::OpenFileByIdentifier`], and unlike them it names nothing by path or
9//! identifier: it can only reach what an open handle already reaches, which is what makes it the
10//! way to *change how* a file is open, such as adding `FILE_FLAG_NO_BUFFERING` to a handle that was
11//! opened without it, without the race a second open by path would run.
12//!
13//! # The original is an input handle, and is shared
14//!
15//! As with the volume hint of [`crate::open_by_id::OpenFileByIdentifier`], the request holds a share
16//! of the handle it reopens, so the request cannot be left naming a handle its originator has
17//! closed. It is the original's own handle, so what the call sees of it is the open the original
18//! belongs to.
19//!
20//! # Nothing is defaulted
21//!
22//! The access, the share mode and the flags all start at zero, "the caller said nothing", and are
23//! carried verbatim (D-12). What a combination means, and which of the flags
24//! `ReOpenFile` accepts, is the call's to say, and a combination it refuses reaches the caller as its
25//! own code. In particular an access or sharing that conflicts with an open still open is the
26//! kernel's sharing violation, and nothing here infers anything from it.
27//!
28//! # Directories
29//!
30//! Every reopen of a directory handle that was tried on the machine this was written on, whatever
31//! the access, share mode and flags, came back as `ERROR_ACCESS_DENIED`. Why is not established,
32//! and the entry does not special-case it: the code is the call's.
33
34use std::ffi::c_void;
35use std::os::windows::io::{FromRawHandle, OwnedHandle};
36
37use windows_sys::Win32::Storage::FileSystem::{
38 FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, ReOpenFile as Win32ReOpenFile,
39};
40
41use win_shared_os_owned_handle::SharedHandle;
42
43use crate::handle::SharedRaw;
44use crate::outcome::{Outcome, perform_handle};
45
46/// An owned, marshalable parameter set for `ReOpenFile`.
47///
48/// # Example
49///
50/// A read-only handle to a file that was opened for writing, the same file through a new handle that
51/// says what it wants anew:
52///
53/// ```
54/// use std::fs;
55///
56/// use windows_namespace_request_sys::reopen::ReOpenFile;
57/// use windows_sys::Win32::Storage::FileSystem::{
58/// FILE_GENERIC_READ, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE,
59/// };
60///
61/// let path = std::env::temp_dir().join(format!("wnrs-reopen-{}.tmp", std::process::id()));
62/// fs::write(&path, b"example")?;
63/// let original = fs::OpenOptions::new().read(true).write(true).open(&path)?;
64///
65/// let request = ReOpenFile::new(original)
66/// .with_desired_access(FILE_GENERIC_READ)
67/// .with_share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);
68/// let reopened = fs::File::from(request.perform()?);
69///
70/// assert_eq!(reopened.metadata()?.len(), b"example".len() as u64);
71/// # drop(reopened);
72/// # fs::remove_file(&path)?;
73/// # Ok::<(), Box<dyn std::error::Error>>(())
74/// ```
75#[derive(Clone, Debug)]
76#[must_use = "an unperformed request reopens nothing"]
77pub struct ReOpenFile {
78 original: SharedHandle,
79 desired_access: u32,
80 share_mode: FILE_SHARE_MODE,
81 flags_and_attributes: FILE_FLAGS_AND_ATTRIBUTES,
82}
83
84impl ReOpenFile {
85 /// Begins a request to reopen the file, directory or volume `original` names.
86 ///
87 /// The request holds `original` as one more holder of it. The remaining parameters start at
88 /// "the caller said nothing" and are set explicitly.
89 pub fn new(original: impl Into<SharedHandle>) -> Self {
90 Self {
91 original: original.into(),
92 desired_access: 0,
93 share_mode: 0,
94 flags_and_attributes: 0,
95 }
96 }
97
98 /// Sets `dwDesiredAccess`.
99 pub fn with_desired_access(mut self, desired_access: u32) -> Self {
100 self.desired_access = desired_access;
101 self
102 }
103
104 /// Sets `dwShareMode`.
105 pub fn with_share_mode(mut self, share_mode: FILE_SHARE_MODE) -> Self {
106 self.share_mode = share_mode;
107 self
108 }
109
110 /// Sets `dwFlagsAndAttributes`, carried verbatim as in [`crate::open::OpenFile`].
111 pub fn with_flags_and_attributes(
112 mut self,
113 flags_and_attributes: FILE_FLAGS_AND_ATTRIBUTES,
114 ) -> Self {
115 self.flags_and_attributes = flags_and_attributes;
116 self
117 }
118
119 /// The handle being reopened, shared with its other holders.
120 pub fn original(&self) -> &SharedHandle {
121 &self.original
122 }
123
124 /// The requested access mask.
125 #[must_use]
126 pub fn desired_access(&self) -> u32 {
127 self.desired_access
128 }
129
130 /// The requested share mode.
131 #[must_use]
132 pub fn share_mode(&self) -> FILE_SHARE_MODE {
133 self.share_mode
134 }
135
136 /// The requested flags and attributes.
137 #[must_use]
138 pub fn flags_and_attributes(&self) -> FILE_FLAGS_AND_ATTRIBUTES {
139 self.flags_and_attributes
140 }
141
142 /// Performs the reopen on the calling thread.
143 ///
144 /// The handle comes back owned and is a new handle each time, however often the request is
145 /// performed.
146 ///
147 /// # Errors
148 ///
149 /// Returns the raw Win32 code, unaltered. An access or sharing that conflicts with a previous
150 /// open still open is a sharing violation, which is the kernel's error and passes through.
151 pub fn perform(&self) -> Outcome<OwnedHandle> {
152 let raw = perform_handle(|| {
153 // SAFETY: the original is one this request holds and keeps open across the
154 // call; the other three arguments are plain values.
155 unsafe {
156 Win32ReOpenFile(
157 self.original.raw(),
158 self.desired_access,
159 self.share_mode,
160 self.flags_and_attributes,
161 )
162 }
163 })?;
164
165 // SAFETY: a successful ReOpenFile returns a handle this process owns exclusively and must
166 // release with CloseHandle, which OwnedHandle does.
167 Ok(unsafe { OwnedHandle::from_raw_handle(raw.cast::<c_void>()) })
168 }
169}
170
171impl crate::request::Request for ReOpenFile {
172 type Error = crate::Win32Error;
173 type Output = OwnedHandle;
174
175 fn perform(&self) -> Outcome<OwnedHandle> {
176 Self::perform(self)
177 }
178}
179
180#[cfg(test)]
181mod tests;