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 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;