Skip to main content

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;