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 owned
14//!
15//! As with the volume hint of [`crate::open_by_id::OpenFileByIdentifier`], the request owns a
16//! duplicate of the handle it reopens, so the request cannot be left naming a handle its originator
17//! has closed. A duplicate names the same file object as the original, so what the call sees of it
18//! is the open the original 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 crate::handle::{CapturedHandle, HandleCaptureError};
42use crate::outcome::{Outcome, perform_handle};
43
44/// An owned, marshalable parameter set for `ReOpenFile`.
45///
46/// # Example
47///
48/// A read-only handle to a file that was opened for writing, the same file through a new handle that
49/// says what it wants anew:
50///
51/// ```
52/// use std::fs;
53/// use std::os::windows::io::AsHandle;
54///
55/// use windows_namespace_request_sys::reopen::ReOpenFile;
56/// use windows_namespace_request_sys::CapturedHandle;
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(CapturedHandle::capture(original.as_handle())?)
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((original, reopened));
72/// # fs::remove_file(&path)?;
73/// # Ok::<(), Box<dyn std::error::Error>>(())
74/// ```
75#[derive(Debug)]
76#[must_use = "an unperformed request reopens nothing"]
77pub struct ReOpenFile {
78 original: CapturedHandle,
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 remaining parameters start at "the caller said nothing" and are set explicitly.
88 pub fn new(original: CapturedHandle) -> Self {
89 Self {
90 original,
91 desired_access: 0,
92 share_mode: 0,
93 flags_and_attributes: 0,
94 }
95 }
96
97 /// Sets `dwDesiredAccess`.
98 pub fn with_desired_access(mut self, desired_access: u32) -> Self {
99 self.desired_access = desired_access;
100 self
101 }
102
103 /// Sets `dwShareMode`.
104 pub fn with_share_mode(mut self, share_mode: FILE_SHARE_MODE) -> Self {
105 self.share_mode = share_mode;
106 self
107 }
108
109 /// Sets `dwFlagsAndAttributes`, carried verbatim as in [`crate::open::OpenFile`].
110 pub fn with_flags_and_attributes(
111 mut self,
112 flags_and_attributes: FILE_FLAGS_AND_ATTRIBUTES,
113 ) -> Self {
114 self.flags_and_attributes = flags_and_attributes;
115 self
116 }
117
118 /// The owned duplicate of the handle being reopened.
119 pub fn original(&self) -> &CapturedHandle {
120 &self.original
121 }
122
123 /// The requested access mask.
124 #[must_use]
125 pub fn desired_access(&self) -> u32 {
126 self.desired_access
127 }
128
129 /// The requested share mode.
130 #[must_use]
131 pub fn share_mode(&self) -> FILE_SHARE_MODE {
132 self.share_mode
133 }
134
135 /// The requested flags and attributes.
136 #[must_use]
137 pub fn flags_and_attributes(&self) -> FILE_FLAGS_AND_ATTRIBUTES {
138 self.flags_and_attributes
139 }
140
141 /// Copies the request, duplicating the handle.
142 ///
143 /// Not `Clone`, because this request always owns a handle and duplicating one is fallible.
144 ///
145 /// # Errors
146 ///
147 /// Returns the handle-capture failure when the handle cannot be duplicated.
148 pub fn try_clone(&self) -> Result<Self, HandleCaptureError> {
149 Ok(Self {
150 original: self.original.try_clone()?,
151 desired_access: self.desired_access,
152 share_mode: self.share_mode,
153 flags_and_attributes: self.flags_and_attributes,
154 })
155 }
156
157 /// Performs the reopen on the calling thread.
158 ///
159 /// The handle comes back owned and is a new handle each time, however often the request is
160 /// performed.
161 ///
162 /// # Errors
163 ///
164 /// Returns the raw Win32 code, unaltered. An access or sharing that conflicts with a previous
165 /// open still open is a sharing violation, which is the kernel's error and passes through.
166 pub fn perform(&self) -> Outcome<OwnedHandle> {
167 let raw = perform_handle(|| {
168 // SAFETY: the original is a duplicate this request owns and keeps open across the
169 // call; the other three arguments are plain values.
170 unsafe {
171 Win32ReOpenFile(
172 self.original.raw(),
173 self.desired_access,
174 self.share_mode,
175 self.flags_and_attributes,
176 )
177 }
178 })?;
179
180 // SAFETY: a successful ReOpenFile returns a handle this process owns exclusively and must
181 // release with CloseHandle, which OwnedHandle does.
182 Ok(unsafe { OwnedHandle::from_raw_handle(raw.cast::<c_void>()) })
183 }
184}
185
186impl crate::request::Request for ReOpenFile {
187 type Error = crate::Win32Error;
188 type Output = OwnedHandle;
189
190 fn perform(&self) -> Outcome<OwnedHandle> {
191 Self::perform(self)
192 }
193}
194
195#[cfg(test)]
196mod tests;